var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });

// src/module/apps/ModuleReviewDialog.ts
var { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
var ModuleReviewDialog = class _ModuleReviewDialog extends HandlebarsApplicationMixin(ApplicationV2) {
  static {
    __name(this, "ModuleReviewDialog");
  }
  savedModules;
  constructor(savedModules, options = {}) {
    super(options);
    this.savedModules = savedModules;
  }
  static DEFAULT_OPTIONS = {
    id: "midi-module-review",
    classes: ["midi-module-review"],
    window: {
      title: "midi-qol.ModuleReview.Title",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      width: 550,
      height: 600
    },
    actions: {
      selectAll: _ModuleReviewDialog.#onSelectAll,
      deselectAll: _ModuleReviewDialog.#onDeselectAll,
      applyModules: _ModuleReviewDialog.#onApply
    }
  };
  static PARTS = {
    modules: {
      template: "modules/midi-qol/templates/apps/ModuleReviewDialog/moduleReview.hbs",
      scrollable: [""]
    }
  };
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    const modules = [];
    for (const [id, saved] of Object.entries(this.savedModules)) {
      const currentModule = game.modules.get(id);
      let status;
      let canActivate = false;
      let checked = false;
      if (currentModule?.active) {
        status = "active";
      } else if (currentModule) {
        status = "installed";
        canActivate = true;
        checked = true;
      } else {
        status = "missing";
      }
      const currentVersion = currentModule?.version ?? null;
      const versionMatch = currentVersion === saved.version;
      const versionLower = !!(currentVersion && saved.version && !versionMatch && foundry.utils.isNewerVersion(saved.version, currentVersion));
      const badgeKeyMap = {
        active: "midi-qol.ModuleReview.Active",
        installed: "midi-qol.ModuleReview.Installed",
        missing: "midi-qol.ModuleReview.NotInstalled"
      };
      const badgeClassMap = {
        active: "badge-active",
        installed: "badge-installed",
        missing: "badge-missing"
      };
      modules.push({
        id,
        title: saved.title ?? id,
        savedVersion: saved.version ?? "?",
        currentVersion,
        status,
        statusClass: status,
        badgeKey: badgeKeyMap[status],
        badgeClass: badgeClassMap[status],
        canActivate,
        checked,
        versionMatch,
        versionLower
      });
    }
    const statusOrder = { missing: 0, installed: 1, active: 2 };
    modules.sort((a, b) => statusOrder[a.status] - statusOrder[b.status] || a.title.localeCompare(b.title));
    context.modules = modules;
    context.hasMissing = modules.some((m) => m.status === "missing");
    return context;
  }
  static #onSelectAll(event) {
    event.preventDefault();
    const checkboxes = Array.from(this.element.querySelectorAll('input[type="checkbox"]:not(:disabled)'));
    for (const cb of checkboxes) cb.checked = true;
  }
  static #onDeselectAll(event) {
    event.preventDefault();
    const checkboxes = Array.from(this.element.querySelectorAll('input[type="checkbox"]:not(:disabled)'));
    for (const cb of checkboxes) cb.checked = false;
  }
  static async #onApply(event) {
    event.preventDefault();
    const checkboxes = Array.from(this.element.querySelectorAll('input[type="checkbox"]:checked:not(:disabled)'));
    const moduleIds = [];
    for (const cb of checkboxes) {
      const moduleId = cb.dataset.moduleId;
      if (moduleId) moduleIds.push(moduleId);
    }
    if (moduleIds.length === 0) {
      ui.notifications?.info(i18n("midi-qol.ModuleReview.NoChanges"));
      return;
    }
    const moduleConfig = game.settings.get("core", "moduleConfiguration");
    let changed = false;
    for (const id of moduleIds) {
      if (!moduleConfig[id]) {
        moduleConfig[id] = true;
        changed = true;
      }
    }
    if (!changed) {
      ui.notifications?.info(i18n("midi-qol.ModuleReview.NoChanges"));
      return;
    }
    await game.settings.set("core", "moduleConfiguration", moduleConfig);
    ui.notifications?.info(i18n("midi-qol.ModuleReview.ReloadPrompt"));
    this.close();
    foundry.utils.debouncedReload();
  }
};

// src/module/apps/TroubleShooter.ts
var { ApplicationV2: ApplicationV22, DialogV2, HandlebarsApplicationMixin: HandlebarsApplicationMixin2 } = foundry.applications.api;
var minimumMidiVersion = "11.0.7";
var TroubleShooter = class _TroubleShooter extends HandlebarsApplicationMixin2(ApplicationV22) {
  static {
    __name(this, "TroubleShooter");
  }
  static errors = [];
  static MAX_ERRORS = 10;
  static _data;
  static set data(data) {
    this._data = data;
  }
  static get data() {
    return this._data;
  }
  // @ts-expect-error will be replaced in v2 anyway (which I have yet to do)
  _fixerId;
  // @ts-expect-error will be replaced in v2 anyway (which I have yet to do)
  _fixerFuncs;
  get nextFixerId() {
    this._fixerId += 1;
    return this._fixerId;
  }
  _hookId;
  constructor(...args) {
    super(...args);
    _TroubleShooter.data = _TroubleShooter.collectTroubleShooterData();
    this._hookId = Hooks.on("midi-qol.TroubleShooter.recordError", (errorDetail) => {
      if (_TroubleShooter.data.isLocal) {
        _TroubleShooter.data = _TroubleShooter.collectTroubleShooterData();
        this.render({ force: true });
      }
    });
    return this;
  }
  // async render(options?) {
  //   await super.render(options);
  //   if (options.tab) this._tabs[0].activate(options.tab);
  //   return this;
  // }
  static DEFAULT_OPTIONS = {
    id: "midi-trouble-shooter",
    classes: ["midi-trouble-shooter"],
    window: {
      title: "midi-qol.TroubleShooter.Label",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      height: "auto",
      width: 900
    },
    form: {
      closeOnSubmit: false
    },
    actions: {
      export: _TroubleShooter.#onExport,
      import: _TroubleShooter.#onImport,
      refresh: _TroubleShooter.#onRefresh,
      clear: _TroubleShooter.#onClear,
      overwrite: _TroubleShooter.#onOverwrite,
      reviewModules: _TroubleShooter.#onReviewModules
    }
  };
  static PARTS = {
    tabs: { template: "templates/generic/tab-navigation.hbs" },
    summary: { template: "modules/midi-qol/templates/apps/TroubleShooter/summary.hbs" },
    common: { template: "modules/midi-qol/templates/apps/TroubleShooter/common.hbs" },
    problems: { template: "modules/midi-qol/templates/apps/TroubleShooter/problems.hbs" },
    errors: { template: "modules/midi-qol/templates/apps/TroubleShooter/errors.hbs" },
    modules: { template: "modules/midi-qol/templates/apps/TroubleShooter/modules.hbs" },
    footer: { template: "templates/generic/form-footer.hbs" }
  };
  // V13 version:
  // static TABS = {
  //   sheet: {
  //     tabs: [
  //       {id: "summary"},
  //       {id: "common"},
  //       {id: "problems"},
  //       {id: "errors"},
  //       {id: "modules"}
  //     ],
  //     initial: "summary",
  //     labelPrefix: "midi-qol.TroubleShooter.Tabs"
  //   }
  // }
  // V12 version:
  tabGroups = {
    sheet: "summary"
  };
  static recordError(err, message) {
    if (!this.errors) this.errors = [];
    while (this.errors.length >= this.MAX_ERRORS) this.errors.shift();
    const timestamp = Date.now();
    const timeString = `${new Date(timestamp).toLocaleDateString()} - ${new Date(timestamp).toLocaleTimeString()}`;
    const stack = err.stack?.split("\n").map((s) => removeIpAddressAndHostName(s));
    const errorDetail = { timestamp, timeString, error: { message: err.message, stack }, message };
    this.errors.push(errorDetail);
    Hooks.callAll("midi-qol.TroubleShooter.recordError", errorDetail);
  }
  static clearErrors() {
    this.errors = [];
  }
  static logErrors() {
  }
  async _updateObject(event, formData) {
  }
  static exportTroubleShooterData() {
    const data = _TroubleShooter.collectTroubleShooterData();
    const filename = "fvtt-midi-qol-troubleshooter.json";
    foundry.utils.saveDataToFile(JSON.stringify(data, null, 2), "text/json", filename);
  }
  static async importTroubleShooterDataFromJSONDialog() {
    const content = await foundry.applications.handlebars.renderTemplate(
      "templates/apps/import-data.hbs",
      { hint1: "Choose a Trouble Shooter JSON file to import" }
    );
    let dialog = new Promise((resolve, reject) => {
      new DialogV2({
        window: { title: `Import Trouble Shooter Data` },
        classes: ["midi-qol-import-troubleshooter"],
        content,
        position: {
          width: 400,
          height: "auto"
        },
        buttons: [
          {
            action: "import",
            icon: "fa-solid fa-file-import",
            label: "Import",
            default: true,
            callback: /* @__PURE__ */ __name((event) => {
              const form = event.currentTarget?.querySelector("form");
              if (!form?.data.files.length) return ui.notifications?.error("You did not upload a data file!");
              foundry.utils.readTextFromFile(form.data.files[0]).then((json) => {
                const jsonData = JSON.parse(json);
                if (foundry.utils.isNewerVersion(minimumMidiVersion, jsonData.midiVersion ?? "0.0.0")) {
                  ui.notifications?.error("Trouble Shooter Data is too old to use");
                  resolve(false);
                  return;
                }
                jsonData.isLocal = false;
                jsonData.fileName = form.data.files[0].name;
                _TroubleShooter.data = jsonData;
                for (let error5 of _TroubleShooter.data.errors) {
                  error5.timeString = `${new Date(error5.timestamp).toLocaleDateString()} - ${new Date(error5.timestamp).toLocaleTimeString()}`;
                }
                const midiSummary = _TroubleShooter.data.summary.midiSettings;
                Object.keys(midiSummary).forEach((key) => {
                  if (typeof midiSummary[key] !== "string") midiSummary[key] = `${midiSummary[key]}`;
                });
                resolve(true);
              });
            }, "callback")
          },
          {
            action: "no",
            icon: "fa-solid fa-xmark",
            label: "Cancel",
            callback: /* @__PURE__ */ __name((event) => resolve(false), "callback")
          }
        ]
      }).render({ force: true });
    });
    return await dialog;
  }
  static getDetailedSettings(moduleId) {
    const returnValue = {};
    let settings = Array.from(game.settings.settings ?? []).filter((i) => i[0].includes(moduleId) && i[1].namespace === moduleId);
    settings.forEach((i) => {
      if (typeof i[1].name !== "string") return;
      if (!i[1].config) return;
      let value = safeGetGameSetting(moduleId, i[1].key);
      if (typeof value !== "string") value = JSON.stringify(value);
      returnValue[i18n(i[1].name)] = value;
    });
    return returnValue;
  }
  static async troubleShooter(app) {
    await _TroubleShooter.exportTroubleShooterData();
  }
  async _onSubmit(...args) {
    let [event, options] = args;
    return {};
  }
  async _preClose(options) {
    await super._preClose(options);
    Hooks.off("midi-qol.TroubleShooter.recordError", this._hookId);
  }
  static #onExport(event) {
    event.preventDefault();
    event.stopPropagation();
    _TroubleShooter.exportTroubleShooterData();
  }
  static async #onImport(event) {
    event.preventDefault();
    event.stopPropagation();
    if (await _TroubleShooter.importTroubleShooterDataFromJSONDialog()) {
      this.render({ force: true });
    }
  }
  static #onRefresh(event) {
    event.preventDefault();
    event.stopPropagation();
    _TroubleShooter.data = _TroubleShooter.collectTroubleShooterData();
    this.render({ force: true });
  }
  static #onClear(event) {
    event.preventDefault();
    event.stopPropagation();
    _TroubleShooter.clearErrors();
    _TroubleShooter.data = _TroubleShooter.collectTroubleShooterData();
    this.render({ force: true });
  }
  static #onOverwrite(event) {
    event.preventDefault();
    event.stopPropagation();
    this.overWriteMidiSettings();
  }
  static #onReviewModules(event) {
    event.preventDefault();
    event.stopPropagation();
    const modules = _TroubleShooter.data?.modules;
    if (!modules || Object.keys(modules).length === 0) {
      ui.notifications?.warn(i18n("midi-qol.TroubleShooter.NoModuleData"));
      return;
    }
    const activeModules = Object.fromEntries(
      Object.entries(modules).filter(([_, mod]) => mod.active)
    );
    if (Object.keys(activeModules).length === 0) {
      ui.notifications?.warn(i18n("midi-qol.TroubleShooter.NoModuleData"));
      return;
    }
    new ModuleReviewDialog(activeModules).render(true);
  }
  async _onRender(context, options) {
    super._onRender(context, options);
    for (let i = 0; i < this._fixerFuncs.length; i++) {
      const id = `#fixer-${i + 1}`;
      const fixerFunc = this._fixerFuncs[i];
      const app = this;
      this.element.querySelector(id)?.addEventListener("click", function(event) {
        event.preventDefault();
        event.stopPropagation();
        fixerFunc(app);
      });
    }
  }
  async overWriteMidiSettings() {
    if (!game.user?.isGM) {
      ui.notifications?.error("Only a GM can overwrite midi settings");
      return false;
    }
    if (_TroubleShooter.data.isLocal) {
      ui.notifications?.warn(`midi-qol | Cant set midi settings - you have not loaded external trouble shooter data`);
      return false;
    }
    let dialog = new Promise((resolve, reject) => {
      new DialogV2({
        window: { title: `Overwrite midi-qol settings from loaded file` },
        content: `<p>This will <strong><em>permanently</em></strong> overwrite your midi settings</p>`,
        position: {
          width: 400,
          height: "auto"
        },
        buttons: [
          {
            action: "overwrite",
            icon: "fa-solid fa-file-import",
            label: "Overwrite",
            callback: /* @__PURE__ */ __name(async (event) => {
              await exportSettingsToJSON();
              const settingsJSON = _TroubleShooter.data.midiSettings;
              importSettingsFromJSON(settingsJSON);
              Hooks.callAll("midi-qol.configSettingsChanged");
              resolve(true);
            }, "callback")
          },
          {
            action: "cancel",
            default: true,
            icon: "fa-solid fa-xmark",
            label: "Cancel",
            callback: /* @__PURE__ */ __name((event) => resolve(false), "callback")
          }
        ]
      }).render({ force: true });
    });
    return await dialog;
  }
  async _prepareContext(options) {
    let context = foundry.utils.deepClone(_TroubleShooter.data);
    context = foundry.utils.mergeObject(await super._prepareContext(options), context, { inplace: false });
    context.hasIncompatible = context.summary.incompatible.length > 0;
    context.hasOutOfDate = context.summary.outOfDate.length > 0;
    context.hasPossibleOutOfData = context.summary.possibleOutOfDate.length > 0;
    context.hasProblems = context.problems.length > 0;
    context.hasErrors = context.errors.length > 0;
    context.hasFoundryModuleProblems = !foundry.utils.isEmpty(context.summary.foundryModuleIssues);
    this._fixerId = 0;
    this._fixerFuncs = [];
    const excludeFoundryWarnings = true;
    if (excludeFoundryWarnings) {
      for (let key of Object.keys(context.summary.foundryModuleIssues)) {
        const problem = context.summary.foundryModuleIssues[key];
        if (problem.error.length === 0) {
          delete context.summary.foundryModuleIssues[key];
        } else delete problem.warning;
      }
    }
    for (let problem of context.problems) {
      if (problem.problemDetail) problem.problemDetail = JSON.stringify(problem.problemDetail);
      if (problem.fixerFunc) {
        problem.hasFixerFunc = true;
        problem.fixerId = this.nextFixerId;
        this._fixerFuncs.push(problem.fixerFunc);
      }
    }
    context.buttons = [
      {
        type: "button",
        action: "refresh",
        icon: "fa-solid fa-gears",
        label: "MENU.Reload"
      },
      {
        type: "button",
        action: "clear",
        icon: "fa-solid fa-gears",
        label: "midi-qol.TroubleShooter.ClearErrors"
      },
      {
        type: "button",
        action: "export",
        icon: "fa-solid fa-floppy-disk",
        label: "SIDEBAR.Export"
      },
      {
        type: "button",
        action: "import",
        icon: "fa-solid fa-file-import",
        label: "SIDEBAR.Import"
      },
      {
        type: "button",
        action: "overwrite",
        icon: "fa-solid fa-download",
        label: "midi-qol.TroubleShooter.Overwrite"
      },
      {
        type: "button",
        action: "reviewModules",
        icon: "fa-solid fa-cubes",
        label: "midi-qol.TroubleShooter.ReviewModules"
      }
    ];
    context.tabs = this.#getTabs();
    return context;
  }
  // V12-only:
  #getTabs() {
    const tabs = {
      summary: { id: "summary", group: "sheet", label: "midi-qol.TroubleShooter.Tabs.summary", active: true },
      common: { id: "common", group: "sheet", label: "midi-qol.TroubleShooter.Tabs.common" },
      problems: { id: "problems", group: "sheet", label: "midi-qol.TroubleShooter.Tabs.problems" },
      errors: { id: "errors", group: "sheet", label: "midi-qol.TroubleShooter.Tabs.errors" },
      modules: { id: "modules", group: "sheet", label: "midi-qol.TroubleShooter.Tabs.modules" }
    };
    return tabs;
  }
  // V12-only (I think):
  async _preparePartContext(partId, context) {
    if (Object.keys(context.tabs).includes(partId)) {
      context.tab = context.tabs[partId];
    }
    return context;
  }
  static collectTroubleShooterData() {
    let data = {
      midiVersion: game.modules.get("midi-qol")?.version,
      isLocal: true,
      fileName: "Local Settings",
      summary: {},
      problems: [],
      modules: {},
      errors: {},
      midiSettings: {}
    };
    const gameVersion = game.version ?? "unknown";
    const gameSystemId = game.system?.id;
    if (!gameSystemId) return data;
    data.summary.gameSystemId = gameSystemId;
    data.summary = {
      "foundry-version": gameVersion,
      "Game System": gameSystemId,
      "Game System Version": game.system?.version,
      "midi-qol-version": game.modules.get("midi-qol")?.version,
      "Dynamic Active Effects Version": game.modules.get("dae")?.version,
      "coreSettings": {
        "Photo Sensitivity": safeGetGameSetting("core", "photosensitiveMode")
      },
      "gameSystemSettings": {
        "Diagonal Distance Setting": safeGetGameSetting(gameSystemId, "diagonalMovement"),
        "Proficiency Variant": safeGetGameSetting(gameSystemId, "proficiencyModifier"),
        "Collapse Item Cards": safeGetGameSetting(gameSystemId, "autoCollapseItemCards"),
        "Critical Damage Maximize Dice": safeGetGameSetting(gameSystemId, "criticalDamageMaxDice"),
        "Critical Damage Modifiers": safeGetGameSetting(gameSystemId, "criticalDamageModifiers"),
        "Concentration Disabled": safeGetGameSetting(gameSystemId, "disableConcentration"),
        "Attack Visibility": safeGetGameSetting(gameSystemId, "attackRollVisibility"),
        "Challenge Visibility": safeGetGameSetting(gameSystemId, "challengeVisibility")
      },
      "moduleSettings": {}
    };
    if (canvas.scene) {
      const globalIllumination = canvas.scene?.environment?.globalLight?.enabled;
      data.summary["coreSettings"]["Scene Details"] = `${canvas.scene.dimensions.height} x ${canvas.scene.dimensions.width} | Size: ${canvas.scene.grid.size} | Type: ${Object.keys(CONST.GRID_TYPES)[canvas.scene.grid.type]} | Distance: ${canvas.scene.grid.distance} | Global Illumination ${globalIllumination}`;
      const sceneObjects = ["tokens", "sounds", "tiles", "walls", "lights", "templates", "notes"];
      const report2 = [];
      for (let c of sceneObjects) {
        const collection = canvas.scene[c];
        report2[c] = `${c} ${collection.size}${collection.invalidDocumentIds.size > 0 ? ` (${collection.invalidDocumentIds.size} ${i18n("Invalid")})` : ""}`;
      }
      data.summary["coreSettings"]["Scene Objects"] = Object.values(report2).join(" | ");
    }
    const report = [];
    const reportCollections = ["actors", "items", "journal", "tables", "playlists", "messages"];
    for (let c of reportCollections) {
      const collection = game[c];
      report[c] = `${c} ${collection.size}${collection.invalidDocumentIds.size > 0 ? ` (${collection.invalidDocumentIds.size} ${i18n("Invalid")})` : ""}`;
    }
    data.summary["coreSettings"]["World Object counts"] = Object.values(report).join(" | ");
    data.summary["coreSettings"]["Module Count"] = `Active: ${game.modules.filter((m) => m.active).length} | Installed: ${game.modules.size}`;
    if (game.modules.get("ActiveAuras")?.active) {
      data.summary.moduleSettings["Active Auras In Combat"] = safeGetGameSetting("ActiveAuras", "combatOnly");
    }
    if (game.modules.get("ddb-importer")?.active) {
    } else data.summary.moduleSettings["DDB Importer"] = i18n("midi-qol.Inactive");
    if (game.modules.get("dfreds-convenient-effects")?.active) {
      data.summary.moduleSettings["Convenient Effects Modify Status Effects"] = safeGetGameSetting("dfreds-convenient-effects", "modifyStatusEffects");
    } else data.summary.moduleSettings["Convenient Effects"] = i18n("midi-qol.Inactive");
    if (game.modules.get("monks-little-details")?.active) {
      data.summary.moduleSettings["Monk's Little Details Status Effects"] = safeGetGameSetting("monks-little-details", "add-extra-statuses");
      data.summary.moduleSettings["Monk's Little Clear Targets"] = safeGetGameSetting("monks-little-details", "clear-targets");
      data.summary.moduleSettings["Monk's Little Remember Targets"] = safeGetGameSetting("monks-little-details", "remember-previous");
    } else data.summary.moduleSettings["Monk's Little Details"] = i18n("midi-qol.Inactive");
    if (game.modules.get("monks-tokenbar")?.active) {
      data.summary.moduleSettings["Monk's Token Bar Allow Players to use"] = safeGetGameSetting("monks-tokenbar", "allow-player");
    } else data.summary.moduleSettings["Monks Token Bar"] = i18n("midi-qol.Inactive");
    if (game.modules.get("flash-rolls-5e")?.active) {
      data.summary.moduleSettings["Flash Token Bar Active"] = "true";
    } else data.summary.moduleSettings["Flash Token Bar"] = i18n("midi-qol.Inactive");
    if (game.modules.get("sequencer")?.active) {
      data.summary.moduleSettings["Sequencer Enable Effects"] = safeGetGameSetting("sequencer", "effectsEnabled");
      data.summary.moduleSettings["Sequencer Enable Sounds"] = safeGetGameSetting("sequencer", "soundsEnabled");
    } else data.summary.moduleSettings["Sequencer"] = i18n("midi-qol.Inactive");
    if (game.modules.get("times-up")?.active) {
      data.summary.moduleSettings["Times Up Disable Passive Effects Expiry"] = safeGetGameSetting("times-up", "DisablePassiveEffects");
    } else data.summary.moduleSettings["Times-Up"] = i18n("midi-qol.Inactive");
    if (game.modules.get("tokenmagic")?.active) {
      data.summary.moduleSettings["Token Magic FX Automatic Template Effects "] = safeGetGameSetting("tokenmagic", "autoTemplateEnabled");
      data.summary.moduleSettings["Token Magic FX Default Template Grid on Hover "] = safeGetGameSetting("tokenmagic", "defaultTemplateOnHover");
      data.summary.moduleSettings["Token Magic FX Auto Hide Template Elements "] = safeGetGameSetting("tokenmagic", "autohideTemplateElements");
    } else data.summary.moduleSettings["Token Magic FX"] = i18n("midi-qol.Inactive");
    const optLabel = /* @__PURE__ */ __name((v) => typeof v === "string" ? v : v?.label ?? "", "optLabel");
    data.summary.midiSettings = {};
    data.summary.midiSettings["Enable Roll Automation Support (Client Setting)"] = `${enableWorkflow}`;
    data.summary.midiSettings["Auto Target on Template Draw"] = optLabel(autoTargetOptions[configSettings.autoTarget]);
    data.summary.midiSettings["Auto Target for Ranged Targets/Spells"] = optLabel(rangeTargetOptions[configSettings.rangeTarget]);
    data.summary.midiSettings["Auto Apply Item Effects"] = optLabel(autoEffectsOptions[configSettings.autoItemEffects]);
    data.summary.midiSettings["Apply Convenient Effects"] = optLabel(autoCEEffectsOptions[configSettings.autoCEEffects]);
    data.summary.midiSettings["Auto Check Hits"] = optLabel(autoCheckHitOptions[configSettings.autoCheckHit]);
    data.summary.midiSettings["Roll Seperate Attacks per Target"] = `${configSettings.attackPerTarget}`;
    data.summary.midiSettings["Auto Check Saves"] = optLabel(autoCheckSavesOptions[configSettings.autoCheckSaves]);
    data.summary.midiSettings["Auto Apply Damage to Target"] = optLabel(autoApplyDamageOptions[configSettings.autoApplyDamage]);
    data.summary.midiSettings["Expire 1Hit/1Attack/1Action on roll"] = `${checkMechanic("actionSpecialDurationImmediate")}`;
    data.summary.midiSettings["Inapacitated Actors can't Take Actions"] = `${checkMechanic("incapacitated")}`;
    data.summary.midiSettings["Calculate Cover"] = optLabel(coverCalculationOptions[configSettings.optionalRules.coverCalculation]);
    data.summary.midiSettings["Add Fake GM Dice"] = `${configSettings.addFakeDice}`;
    data.summary.knownModules = {};
    let tempModules = {};
    checkedModuleList.forEach((matcher) => {
      const modules = game.modules.filter((m) => !!m.id.match(matcher)) ?? [];
      if (modules.length > 0) {
        modules.forEach((module) => {
          foundry.utils.setProperty(tempModules, module.id, { title: module.title, active: module.active, installed: true, moduleVersion: module.version, foundryVersion: module.compatibility?.verified });
        });
      } else {
        foundry.utils.setProperty(tempModules, matcher.toString(), { title: "Not installed", active: false, installed: false, moduleVersion: ``, foundryVersion: `` });
      }
    });
    const baseVersion = game.version?.slice(0, 2) ?? "unknown";
    const maxVersion = baseVersion + ".999";
    CheckedAuthorsList.forEach((matcher) => {
      const modules = game.modules.filter((m) => m.authors.find((au) => au.name.toLocaleLowerCase().match(matcher))) ?? [];
      if (modules.length > 0) {
        modules.forEach((module) => {
          foundry.utils.setProperty(tempModules, module.id, { title: module.title, active: module.active, installed: true, moduleVersion: module.version, foundryVersion: module.compatibility?.verified });
        });
      }
    });
    Object.keys(tempModules).sort((m1, m2) => m1 < m2 ? -1 : m1 > m2 ? 1 : 0).forEach((key) => {
      data.summary.knownModules[key] = tempModules[key];
    });
    data.modules = collectModuleData();
    for (const id of Object.keys(data.modules)) {
      if (id.match(/xxxxxxxxx/i)) {
        data.summary.moduleSettings["xxxxxxxxx detected"] = "incompatible importer found";
      }
    }
    for (const [moduleId, moduleEntry] of Object.entries(data.modules)) {
      switch (moduleId) {
        case "ATL":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "ActiveAuras":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "about-time":
          break;
        case "anonymous":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "autoanimations":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          if (game.modules.get("autoanimations")?.active) this.checkAutoAnimations(data);
          break;
        case "combat-utility-belt":
          break;
        case "condition-lab-triggler":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "dae":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "ddb-game-log":
          break;
        case "dfreds-convenient-effects":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "dice-so-nice":
          break;
        case "effect-macro":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "foundryvtt=simple-calendar":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "itemacro":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          this.checkItemMacro(data);
          break;
        case "levels":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "levelsautocover":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "levelsvolumetrictemplates":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "lib-wrapper":
          if (!game.modules.get("lib-wrapper")?.active) {
            data.problems.push({
              moduleId: "lib-wrapper",
              severity: "Error",
              critical: true,
              problemSummary: "Midi won't function without lib-wrapper",
              fixer: "Install and activate lib-wrapper",
              problemDetail: void 0
            });
          }
          ;
          break;
        case "midi-qol":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "monks-combat-details":
          if (safeGetGameSetting("monks-combat-details", "auto-defeated") !== "none" && configSettings.addDead !== "none") {
            data.problems.push({
              moduleId: "monks-combat-details",
              severity: "Error",
              problemSummary: "Both Midi and Monks Combat Details are adding defeated effects",
              fixer: "Disable defeated effects in one of the modules",
              problemDetail: void 0
            });
          }
          break;
        case "monks-little-details":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "monks-tokenbar":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "flash-rolls-5e":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "multilevel-tokens":
          break;
        case "simbuls-cover-calculator":
          break;
        case "socketlib":
          if (!game.modules.get("socketlib")?.active) {
            data.problems.push({
              moduleId: "socketlib",
              severity: "Error",
              critical: true,
              problemSummary: "Midi won't function without socketlib",
              fixer: "Install and activate socketlib",
              problemDetail: void 0
            });
          }
          ;
          break;
        case "times-up":
          if (!game.modules.get("times-up")?.active) {
            data.problems.push({
              moduleId: "times-up",
              severity: "Warn",
              problemSummary: "Times Up is not installed or not active. Effects won't expire",
              fixer: "Install and activate times-up",
              problemDetail: void 0
            });
          }
          ;
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "walledtemplates":
          this.checkWalledTemplates(data);
          break;
        case "warpgate":
          foundry.utils.setProperty(data.modules[moduleId], "settings", _TroubleShooter.getDetailedSettings(moduleId));
          break;
        case "wjmaia":
          break;
        case "advancedspelleffects":
        case "attack-roll-check-5e":
        case "betterrolls5e":
        case "dice-rng-protector":
        case "effective-transferral":
        case "fast-rolls":
        case "faster-rolling-by-default-5e":
        case "gm-paranoia-taragnor":
        case "max-crit":
        case "mre-dnd5e":
        case "multiattack-5e":
        case "obsidian":
        case "quick-rolls":
        case "ready-set-roll-5e":
        case "roll-tooltips-5e":
        case "retroactive-advantage-5e":
        case "rollgroups":
        case "wire":
          data.modules[moduleId].incompatible = true;
          break;
      }
    }
    data.summary.incompatible = Object.keys(data.modules).filter((key) => data.modules[key].incompatible && game.modules.get(key)?.active).map((key) => ({ key, title: data.modules[key].title }));
    data.summary.foundryModuleIssues = foundry.utils.duplicate(game.issues?.packageCompatibilityIssues);
    for (let key in data.summary.foundryModuleIssues) {
      const issue = data.summary.foundryModuleIssues[key];
      issue.title = game.modules.get(key)?.title;
      delete issue.manifest;
    }
    data.summary.outOfDate = Object.keys(data.modules).filter((key) => foundry.utils.isNewerVersion(baseVersion, data.modules[key].compatibility ?? 0)).map((key) => {
      const versionString = `${data.modules[key].active ? i18n("midi-qol.Active") : i18n("midi-qol.Inactive")} ${data.modules[key].version}`;
      return {
        key,
        title: data.modules[key].title,
        active: data.modules[key].active,
        moduleVersion: data.modules[key].version,
        //versionString,
        foundryVersion: data.modules[key].compatibility
      };
    });
    data.summary.possibleOutOfDate = Object.keys(data.modules).filter((key) => {
      let moduleVersion = data.modules[key].compatibility ?? "0.0.0";
      if (moduleVersion === baseVersion) moduleVersion = maxVersion;
      if (foundry.utils.isNewerVersion(baseVersion, moduleVersion)) return false;
      return foundry.utils.isNewerVersion(gameVersion, moduleVersion);
    }).map((key) => ({
      key,
      title: data.modules[key].title,
      active: data.modules[key].active,
      moduleVersion: data.modules[key].version,
      version: data.modules[key].compatibility
    }));
    for (let key of Object.keys(USEFUL_MODULE_VERSIONS)) {
      if (game.modules.get(key)?.active) {
        const installedVersion = getModuleVersion(key);
        const requiredVersion = USEFUL_MODULE_VERSIONS[key];
        if (foundry.utils.isNewerVersion(requiredVersion, installedVersion)) {
          data.problems.push({
            moduleId: key,
            severity: "Error",
            problemSummary: `${key} needs to be at least version ${requiredVersion} but is version ${installedVersion} and will not be used`,
            fixer: `Update ${key} to latest version`,
            problemDetail: void 0
          });
        }
      }
    }
    data.summary.foundryReportedErrors;
    let midiSettings = foundry.utils.duplicate(collectSettingData());
    delete midiSettings.flags;
    data.midiSettings = midiSettings;
    _TroubleShooter.checkCommonProblems(data);
    data.errors = foundry.utils.duplicate(_TroubleShooter.errors).reverse();
    return data;
  }
  static checkConcentrationStatusEffects(data) {
    if (safeGetGameSetting(game.system?.id ?? "dnd5e", "disableConcentration")) return;
    let severity = "Error";
    let statusEffect = CONFIG.statusEffects.find((e) => e.id === systemConcentrationId);
    if (!statusEffect) {
      data.problems.push({
        moduleId: "midi-qol",
        severity,
        problemSummary: `Concentration Automation is enabled but the status effect with id "${systemConcentrationId}" was not found.`,
        problemDetail: void 0,
        fixer: "Check which module is removing the concentrating status effect"
      });
    }
  }
  static checkMidiCoverSettings(data) {
    switch (configSettings.optionalRules.wallsBlockRange) {
      case "none":
        break;
      case "center":
        break;
      case "centerLevels":
        if (!game.modules.get("levels")?.active) {
          data.problems.push({
            moduleId: "levels",
            severity: "Error",
            problemSummary: "You must enable the 'levels' module to use the 'Center Levels' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'levels' module"
          });
        }
        break;
      case "levelsautocover":
        if (!game.modules.get("levelsautocover")?.active) {
          data.problems.push({
            moduleId: "levelsautocover",
            severity: "Error",
            problemSummary: "You must enable the 'levelsautocover' module to use the 'Levels Auto Cover' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'levelsautocover' module"
          });
        }
        break;
      case "simbuls-cover-calculator":
        if (!game.modules.get("simbuls-cover-calculator")?.active) {
          data.problems.push({
            moduleId: "simbuls-cover-calculator",
            severity: "Error",
            problemSummary: "You must enable the 'simbuls-cover-calculator' module to use the 'Simbul's Cover Calculator' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'simbuls-cover-calculator' module"
          });
        }
        break;
      case "tokenvisibility":
        data.problems.push({
          moduleId: "tokenvisibility",
          severity: "Error",
          problemSummary: "Midi has swtiched to Alternate Token Cover from Alternate Token Visibility. You should install and activate Alternative Token Cover",
          problemDetail: void 0,
          fixer: "Enable the 'tokencover' module and set 'Walls Block Range' to 'Token Cover' on the Mechanics Tab"
        });
        break;
      case "tokencover":
        if (!game.modules.get("tokencover")?.active) {
          data.problems.push({
            moduleId: "tokencover",
            severity: "Error",
            problemSummary: "You must enable the 'tokencover' module to use the 'Token Cover' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'tokencover' module"
          });
        }
        break;
    }
    switch (configSettings.optionalRules.coverCalculation) {
      case "none":
        break;
      case "levelsautocover":
        if (!game.modules.get("levelsautocover")?.active) {
          data.problems.push({
            moduleId: "levelsautocover",
            severity: "Error",
            problemSummary: "You must enable the 'levelsautocover' module to use the 'Levels Auto Cover' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'levelsautocover' module"
          });
        }
        break;
      case "simbuls-cover-calculator":
        if (!game.modules.get("simbuls-cover-calculator")?.active) {
          data.problems.push({
            moduleId: "simbuls-cover-calculator",
            severity: "Error",
            problemSummary: "You must enable the 'simbuls-cover-calculator' module to use the 'Simbul's Cover Calculator' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'simbuls-cover-calculator' module"
          });
        }
        break;
      case "tokenvisibility":
        data.problems.push({
          moduleId: "tokenvisibility",
          severity: "Error",
          problemSummary: "Midi has swtiched to Alternate Token Cover from Alternate Token Visibility. You should install Alternative Token Cover",
          problemDetail: void 0,
          fixer: "Enable the 'tokencover' module and update 'Calculate Cover' to 'Token Cover' on the Mechanics tab"
        });
        break;
      case "tokencover":
        if (!game.modules.get("tokencover")?.active) {
          data.problems.push({
            moduleId: "tokencover",
            severity: "Error",
            problemSummary: "You must enable the 'tokencover' module to use the 'Token Cover' option for 'Walls Block Range'",
            problemDetail: void 0,
            fixer: "Enable the 'tokencover' module"
          });
        }
        break;
    }
    switch (configSettings.autoTarget) {
      case "walledtemplates":
        if (!game.modules.get("walledtemplates")?.active) {
          data.problems.push({
            moduleId: "walledtemplates",
            severity: "Error",
            problemSummary: "You must enable the 'walledtemplates' module to use the 'Walled Templates' option for 'Auto Target on Template Draw'",
            problemDetail: void 0,
            fixer: "Enable the 'walledtemplates' module"
          });
        }
        break;
    }
  }
  static checkMidiSaveSettings(data) {
    if (!installedModules.get("monks-tokenbar") && (configSettings.playerRollSaves === "mtb" || configSettings.rollNPCSaves === "mtb" || configSettings.rollNPCLinkedSaves === "mtb")) {
      data.problems.push({
        moduleId: "monks-tokenbar",
        severity: "Error",
        problemSummary: "You must enable the 'monks-tokenbar' module to use the 'Monk's Token Bar' option for 'Roll NPC.Player Saves'",
        problemDetail: void 0,
        fixer: "Enable the 'monks-tokenbar' module"
      });
    }
    if (!installedModules.get("flash-rolls-5e") && (configSettings.playerRollSaves === "ftb" || configSettings.rollNPCSaves === "ftb" || configSettings.rollNPCLinkedSaves === "ftb")) {
      data.problems.push({
        moduleId: "flash-rolls-5e",
        severity: "Error",
        problemSummary: "You must enable the 'flash-rolls-5e' module to use the 'Flash Token Bar' option for 'Roll NPC/Player Saves'",
        problemDetail: void 0,
        fixer: "Enable the 'flash-rolls-5e' module"
      });
    }
  }
  static checkWalledTemplates(data) {
    if (game.modules.get("walledtemplates")?.active) {
      const walledTemplatesTargeting = safeGetGameSetting("walledtemplates", "autotarget-menu") === "yes" || safeGetGameSetting("walledtemplates", "autotarget-menu") === "toggle" && safeGetGameSetting("walledtemplates", "autotarget-enabled");
      const midiTargeting = configSettings.autoTarget !== "walledtemplates" && configSettings.autoTarget !== "none";
      if (walledTemplatesTargeting && midiTargeting) {
        data.problems.push({
          moduleId: "walledtemplates",
          severity: "Error",
          problemSummary: "Both walled templates auto targeting and midi's auto targeting are enabled",
          problemDetail: void 0,
          fixer: "Only enable one of the auto targeting options",
          /*          fixerFunc: async function (app: TroubleShooter) {
                      if (!game.user?.isGM) {
                        ui.notifications?.error("midi-qol | You must be a GM to fix walled templates auto target");
                        return;
                      }
                      await game.settings.set("walledtemplates", "autotarget-enabled", true);
                      await game.settings.set("walledtemplates", "autotarget-menu", "yes");
                      configSettings.autoTarget = "walledtemplates";
                      await game.settings.set("midi-qol", "ConfigSettings", configSettings);
                      //@ts-expect-error reload configure
                      SettingsConfig.reloadConfirm({ world: true });
                      TroubleShooter.data = TroubleShooter.collectTroubleShooterData();
                      app.render(true)
                    },
          */
          fixerId: -1
        });
      } else if (walledTemplatesTargeting && configSettings.autoTarget !== "walledtemplates") {
        data.problems.push({
          moduleId: "walledtemplates",
          severity: "Error",
          problemSummary: "Walled templates is set to auto target and midi is not using it for targeting",
          problemDetail: void 0,
          fixer: "Disable walled templates auto target",
          fixerFunc: /* @__PURE__ */ __name(async function(app) {
            if (!game.user?.isGM) {
              ui.notifications?.error("midi-qol | You must be a GM to fix walled templates settings");
              return;
            }
            await game.settings.set("walledtemplates", "autotarget-enabled", false);
            await game.settings.set("walledtemplates", "autotarget-menu", "no");
            _TroubleShooter.data = _TroubleShooter.collectTroubleShooterData();
            app.render(true);
          }, "fixerFunc"),
          fixerId: -1
        });
      }
    } else if (configSettings.autoTarget === "walledtemplates") {
      data.problems.push({
        moduleId: "walledtemplates",
        severity: "Error",
        problemSummary: "Midi is set to use walled templates but the module is not enabled",
        problemDetail: void 0,
        fixer: "Enable the walled templates module",
        fixerId: -1
      });
    }
  }
  static checkItemMacro(data) {
    if (!game.modules.get("itemacro")?.active) return;
    if (safeGetGameSetting("itemacro", "charsheet")) {
      data.problems.push({
        moduleId: "itemacro",
        severity: "Warn",
        problemSummary: "Item Macro Character sheet hook is enabled.",
        problemDetail: void 0,
        fixer: "Turn off the setting in module settings or use the auto fix button",
        fixerFunc: /* @__PURE__ */ __name(async function(app) {
          if (!game.user?.isGM) {
            ui.notifications?.error("midi-qol | You must be a GM to fix Item Macro char sheet flag");
            return;
          }
          await game.settings.set("itemacro", "charsheet", false);
          SettingsConfig.reloadConfirm({ world: true });
        }, "fixerFunc"),
        fixerId: -1
      });
    }
  }
  static checkCommonProblems(data) {
    this.checkMidiSettings(data);
    this.checkMidiCoverSettings(data);
    this.checkMidiSaveSettings(data);
    this.checkNoActorTokens(data);
    this.checkConcentrationStatusEffects(data);
    this.checkChatLogPrune(data);
  }
  static checkChatLogPrune(data) {
    if (!game.modules.get("chatlog-prune")?.active) {
      data.problems.push({
        moduleId: "chatlog-prune",
        severity: "Warn",
        problemSummary: "Chat Log Prune is not active. Long sessions may experience performance issues as chat messages accumulate.",
        problemDetail: void 0,
        fixer: "Install and activate chatlog-prune"
      });
    }
  }
  static checkAutoAnimations(data) {
  }
  // public static checkWarpgateUserPermissions(data: TroubleShooterData) {
  //   if (!game.permissions?.TOKEN_CREATE.includes(1)) {
  //     const problem: ProblemSpec = {
  //       moduleId: "warpgate",
  //       severity: "Warn",
  //       problemSummary: "Players Do not have permission to create tokens",
  //       problemDetail: undefined,
  //       fixer: "Edit player permissions"
  //     }
  //     data.problems.push(problem);
  //   }
  //   if (!game.permissions?.TOKEN_CONFIGURE.includes(1)) {
  //     const problem: ProblemSpec = {
  //       moduleId: "warpgate",
  //       severity: "Warn",
  //       problemSummary: "Players Do not have permission to configure tokens",
  //       problemDetail: undefined,
  //       fixer: "Edit player permissions"
  //     }
  //     data.problems.push(problem);
  //   }
  //   if (!game.permissions?.FILES_BROWSE.includes(1)) {
  //     const problem: ProblemSpec = {
  //       moduleId: "warpgate",
  //       severity: "Warn",
  //       problemSummary: "Players Do not have permission to browse files",
  //       problemDetail: undefined,
  //       fixer: "Edit player permissions"
  //     }
  //     data.problems.push(problem);
  //   }
  // }
  // Check for tokens with no actors
  static checkNoActorTokens(data) {
    const problemTokens = canvas.tokens?.placeables.filter((token) => !token.actor);
    if (problemTokens?.length) {
      let problem = {
        moduleId: "midi-qol",
        severity: "Warn",
        problemSummary: "There are tokens with no actor in the scene",
        problemDetail: problemTokens.map((t) => {
          const detail = {};
          detail[`${t.scene?.name ?? ""} - ${t.name}`] = t.document.uuid;
          return detail;
        }),
        fixer: "You should edit or remove them"
      };
      data.problems.push(problem);
    }
  }
  static checkMidiSettings(data) {
    if (!safeGetGameSetting("midi-qol", "EnableWorkflow")) {
      data.problems.push({
        moduleId: "midi-qol",
        severity: "Warn",
        problemSummary: "Combat automation is disabled",
        problemDetail: "Also need to check on all player clients",
        fixerFunc: /* @__PURE__ */ __name(async function(app) {
          game.settings.set("midi-qol", "EnableWorkflow", true).then(() => {
            fetchParams();
            _TroubleShooter.data = _TroubleShooter.collectTroubleShooterData();
            app.render({ force: true });
          });
        }, "fixerFunc"),
        fixerId: -1
      });
    }
  }
};
function removeIpAddressAndHostName(inputString) {
  const urlRegex = /(?:https?|ftp):\/\/([a-zA-Z0-9.-]+)(?::\d+)?(\/[^\s]*)?/gi;
  const sanitisedString = inputString.replace(urlRegex, (match, hostname) => {
    return match.replaceAll(hostname, "<address>");
  });
  return sanitisedString;
}
__name(removeIpAddressAndHostName, "removeIpAddressAndHostName");

// src/module/setupModules.ts
var DAE_REQUIRED_VERSION = "13.0.23";
var USEFUL_MODULE_VERSIONS = {
  "about-time": "0.0",
  "ActiveAuras": "0.4.13",
  "anonymous": "0.0.0",
  "auraeffects": "1.3.4",
  "autoanimations": "3.2.991",
  "chatlog-prune": "1.0.1",
  "chris-premades": "1.3.0",
  "condition-lab-triggler": "1.4",
  "dae": DAE_REQUIRED_VERSION,
  "ddb-game-log": "0.0.0",
  "dfreds-convenient-effects": "5.0.4",
  "dice-so-nice": "4.1.1",
  "epic-rolls-5e": "3.1.1",
  "flash-rolls-5e": "1.13.1",
  "gambits-premades": "2.0.1",
  "levels": "6.0.0",
  "levelsautocover": "1.4",
  "levelsvolumetrictemplates": "0.0.0",
  "lib-changelogs": "0.0.0",
  "lib-wrapper": "1.3.5",
  "magicitems": "4.0.0",
  "midi-item-showcase-community": "1.5",
  "monks-tokenbar": "1.0.55",
  "multilevel-tokens": "13.0.0",
  "perceptive": "3.2.1",
  "ready-set-roll-5e": "3.4.3",
  "seasons-and-stars": "0.11.0",
  "simbuls-cover-calculator": "1.0.2",
  "simplecover5e": "1.3.1",
  "socketlib": "0.0",
  "times-up": "13.0.0",
  "tokencover": "0.6.0",
  "vision-5e": "3.0.6",
  "walledtemplates": "0.0.0",
  "wjmais": "0.0.0"
};
var installedModules = /* @__PURE__ */ new Map();
function getModuleVersion(moduleName) {
  let modVer = game.modules.get(moduleName)?.version || "0.0.0";
  if (!/[0-9\.]+/.test(modVer)) {
    console.warn(`midi-qol | module ${moduleName} has unrecognised version ${modVer} using ${USEFUL_MODULE_VERSIONS[moduleName]} instead}`);
    modVer = USEFUL_MODULE_VERSIONS[moduleName];
  }
  return modVer;
}
__name(getModuleVersion, "getModuleVersion");
var setupModules = /* @__PURE__ */ __name(() => {
  for (let name of Object.keys(USEFUL_MODULE_VERSIONS)) {
    const modVer = getModuleVersion(name);
    const neededVer = USEFUL_MODULE_VERSIONS[name];
    const isValidVersion = foundry.utils.isNewerVersion(modVer, neededVer) || !foundry.utils.isNewerVersion(neededVer, modVer);
    if (!installedModules.get(name)) installedModules.set(name, game.modules.get(name)?.active && isValidVersion);
    if (!installedModules.get(name)) {
      if (game.modules.get(name)?.active) {
        const message = `midi-qol requires ${name} to be of version ${USEFUL_MODULE_VERSIONS[name]} or later, but it is version ${game.modules.get(name)?.version}`;
        error2(message);
        TroubleShooter.recordError(new Error(message), message);
      } else warn(`optional module ${name} not active - some features disabled`);
    }
  }
  if (debugEnabled > 0) {
    for (let module of installedModules.keys())
      log(`module ${module} has valid version ${installedModules.get(module)}`);
  }
}, "setupModules");
function dice3dEnabled() {
  return Boolean(installedModules.get("dice-so-nice") && (game.dice3d?.config?.enabled || game.dice3d?.isEnabled()));
}
__name(dice3dEnabled, "dice3dEnabled");
function checkModules() {
  if (game.user?.isGM && !installedModules.get("socketlib")) {
    ui.notifications?.error("midi-qol.NoSocketLib", { permanent: true, localize: true });
  }
  if (game.user?.isGM && pruneChatLog) {
    const chatlogPruneModule = game.modules.get("chatlog-prune");
    const chatlogPruneActive = chatlogPruneModule?.active;
    const chatlogPruneEnabled = chatlogPruneActive && game.settings.get("chatlog-prune", "enabled");
    if (!chatlogPruneActive || !chatlogPruneEnabled) {
      let message = `<h3>${i18n("midi-qol.chatlogPruneWarning")}</h3>`;
      message += `<p>${i18n("midi-qol.chatlogPruneDeprecated")}</p>`;
      if (!chatlogPruneActive) {
        message += `<p>${i18n("midi-qol.chatlogPruneInstallHint")}</p>`;
      } else if (!chatlogPruneEnabled) {
        message += `<p>${i18n("midi-qol.chatlogPruneEnableHint")}</p>`;
      }
      ChatMessage.implementation.create({
        content: message,
        whisper: game.users?.filter((u) => u.isGM).map((u) => u.id),
        speaker: { alias: "midi-qol" }
      });
    }
  }
}
__name(checkModules, "checkModules");

// src/module/lib/HandlebarsHelpers.ts
function registerHandlebarsHelpers() {
  Handlebars.registerHelper("midiSelectOptions", midiSelectOptions);
}
__name(registerHandlebarsHelpers, "registerHandlebarsHelpers");
function midiSelectOptions(choices, options) {
  const {
    localize = false,
    blank,
    sort,
    valueAttr,
    labelAttr,
    hintAttr,
    inverted
  } = options.hash;
  let selected = normalizeSelected(options.hash.selected);
  const optionList = [];
  if (choices instanceof Array) {
    for (const [i, choice] of choices.entries()) {
      if (typeof choice === "object") {
        optionList.push(extractOption(choice, i, { valueAttr, labelAttr, hintAttr }));
      } else {
        optionList.push({ value: String(i), label: String(choice) });
      }
    }
  } else if (choices && typeof choices === "object") {
    for (const entry of Object.entries(choices)) {
      const [k, v] = inverted ? [entry[1], entry[0]] : entry;
      const value = valueAttr ? v[valueAttr] : k;
      if (typeof v === "object") {
        optionList.push(extractOption(v, value, { valueAttr, labelAttr, hintAttr }));
      } else {
        optionList.push({ value: String(value), label: String(v) });
      }
    }
  }
  if (localize) {
    for (const opt of optionList) {
      if (opt.label) opt.label = game.i18n.localize(opt.label);
      if (opt.hint) {
        const localized = game.i18n.localize(opt.hint);
        opt.hint = localized !== opt.hint ? localized : void 0;
      }
    }
  }
  if (sort) {
    optionList.sort((a, b) => (a.label ?? "").localeCompare(b.label ?? "", game.i18n.lang));
  }
  const parts = [];
  if (typeof blank === "string") {
    const blankLabel = localize ? game.i18n.localize(blank) : blank;
    const sel = selected.includes("") ? " selected" : "";
    parts.push(`<option value=""${sel}>${escapeHTML(blankLabel)}</option>`);
  }
  for (const opt of optionList) {
    if (opt.value === void 0 || opt.label === void 0) continue;
    const sel = selected.includes(String(opt.value)) ? " selected" : "";
    const hintStr = opt.hint ? ` title="${escapeHTML(opt.hint)}"` : "";
    parts.push(`<option value="${escapeHTML(String(opt.value))}"${sel}${hintStr}>${escapeHTML(opt.label)}</option>`);
  }
  return new Handlebars.SafeString(parts.join("\n"));
}
__name(midiSelectOptions, "midiSelectOptions");
function normalizeSelected(selected) {
  if (selected === void 0 || selected === null) return [];
  if (selected instanceof Set) return Array.from(selected).map(String);
  if (selected instanceof Array) return selected.map(String);
  return [String(selected)];
}
__name(normalizeSelected, "normalizeSelected");
function extractOption(obj, fallbackValue, attrs) {
  const value = String(attrs.valueAttr ? obj[attrs.valueAttr] : obj.value ?? fallbackValue);
  const label = String(attrs.labelAttr ? obj[attrs.labelAttr] : obj.label ?? value);
  const hint = attrs.hintAttr ? obj[attrs.hintAttr] : obj.hint;
  const entry = { value, label };
  if (hint !== void 0 && hint !== null) entry.hint = String(hint);
  return entry;
}
__name(extractOption, "extractOption");
function escapeHTML(str) {
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
__name(escapeHTML, "escapeHTML");
function withOptionLabel(option, label) {
  if (typeof option === "object") {
    return { ...option, label };
  }
  return label;
}
__name(withOptionLabel, "withOptionLabel");

// src/module/apps/ConfigPanel.ts
var { ApplicationV2: ApplicationV23, DialogV2: DialogV22, HandlebarsApplicationMixin: HandlebarsApplicationMixin3 } = foundry.applications.api;
var PATH = "./modules/midi-qol/sample-config/";
var ConfigPanel = class extends HandlebarsApplicationMixin3(ApplicationV23) {
  static {
    __name(this, "ConfigPanel");
  }
  configHookId;
  activeTab;
  constructor(options) {
    super(options);
    this.configHookId = Hooks.on("midi-qol.ConfigSettingsChanged", () => {
      this.close();
    });
    this.activeTab = "gm";
    return this;
  }
  static DEFAULT_OPTIONS = {
    id: "midi-qol-settings",
    tag: "form",
    window: {
      title: "midi-qol.ConfigTitle",
      contentClasses: ["standard-form"]
    },
    position: {
      width: 800,
      height: "auto"
    },
    form: {
      closeOnSubmit: true,
      handler: this.#onSubmit
    }
  };
  static PARTS = {
    tabs: { template: "templates/generic/tab-navigation.hbs" },
    gm: { template: "modules/midi-qol/templates/apps/ConfigPanel/gm.hbs" },
    player: { template: "modules/midi-qol/templates/apps/ConfigPanel/player.hbs" },
    workflow: { template: "modules/midi-qol/templates/apps/ConfigPanel/workflow.hbs" },
    concentration: { template: "modules/midi-qol/templates/apps/ConfigPanel/concentration.hbs" },
    reactions: { template: "modules/midi-qol/templates/apps/ConfigPanel/reactions.hbs" },
    misc: { template: "modules/midi-qol/templates/apps/ConfigPanel/misc.hbs" },
    mechanics: { template: "modules/midi-qol/templates/apps/ConfigPanel/mechanics.hbs" },
    rules: { template: "modules/midi-qol/templates/apps/ConfigPanel/rules.hbs" },
    quick: { template: "modules/midi-qol/templates/apps/ConfigPanel/quick.hbs" },
    footer: { template: "modules/midi-qol/templates/apps/ConfigPanel/footer.hbs" }
  };
  // V13 version:
  static TABS = {
    sheet: {
      tabs: [
        { id: "gm" },
        { id: "player" },
        { id: "workflow" },
        { id: "concentration" },
        { id: "reactions" },
        { id: "misc" },
        { id: "mechanics" },
        { id: "rules" },
        { id: "quick" }
      ],
      initial: "gm",
      labelPrefix: "midi-qol.Config.Tabs"
    }
    // This is silly, but for now there's some types-thinks "mandatory" fields 
  };
  // V12 version:
  // tabGroups = {
  //   sheet: "gm"
  // }
  get title() {
    return i18n("midi-qol.ConfigTitle") ?? "Midi-QOL Configuration";
  }
  async _prepareContext(options) {
    if (!enableWorkflow) {
      ui.notifications?.error("Workflow automation is not enabled");
    }
    let wallsBlockRangeOptions = foundry.utils.duplicate(wallsBlockRangeOptionsNew);
    let CoverCalculationOptions = foundry.utils.duplicate(coverCalculationOptions);
    [{ id: "levelsautocover", name: "'Levels Auto Cover'" }, { id: "simbuls-cover-calculator", name: "'Simbuls Cover Calculator'" }, { id: "simplecover5e", name: "'Simple Cover 5e'" }, { id: "tokencover", name: "'Alternative Token Cover'" }].forEach((module) => {
      if (!installedModules.get(module.id)) {
        const notInstalled = game.i18n?.format("MODMANAGE.DepNotInstalled", { missing: "" }) ?? "";
        wallsBlockRangeOptions[module.id] = withOptionLabel(wallsBlockRangeOptions[module.id], `${i18n(wallsBlockRangeOptions[module.id])} - ${notInstalled}`);
        CoverCalculationOptions[module.id] = withOptionLabel(CoverCalculationOptions[module.id], `${i18n(CoverCalculationOptions[module.id])} - ${notInstalled}`);
      }
    });
    if (!installedModules.get("levels")) {
      wallsBlockRangeOptions["centerLevels"] = withOptionLabel(wallsBlockRangeOptions["centerLevels"], `${i18n(wallsBlockRangeOptions["centerLevels"])} - ${game.i18n?.format("MODMANAGE.DepNotInstalled", { missing: "Levels" })}`);
    }
    const autoTargetOptionsLocalized = foundry.utils.duplicate(autoTargetOptions);
    if (!installedModules.get("walledtemplates")) {
      const notInstalled = game.i18n?.format("MODMANAGE.DepNotInstalled", { missing: "" }) ?? "";
      autoTargetOptionsLocalized["walledtemplates"] = withOptionLabel(autoTargetOptions["walledtemplates"], `${i18n(autoTargetOptions["walledtemplates"])} - ${notInstalled}`);
    }
    let HiddenAdvantageOptions = foundry.utils.duplicate(hiddenAdvantageOptions);
    [{ id: "perceptive", name: "Perceptive" }].forEach((module) => {
      if (!installedModules.get(module.id)) {
        HiddenAdvantageOptions[module.id] = withOptionLabel(HiddenAdvantageOptions[module.id], `${i18n(HiddenAdvantageOptions[module.id])} - ${game.i18n?.format("MODMANAGE.DepNotInstalled", { missing: "" })}`);
      }
    });
    let quickSettingsOptions = {};
    for (let key of Object.keys(quickSettingsDetails)) {
      quickSettingsOptions[key] = quickSettingsDetails[key].description;
    }
    const AddWoundedOptions = foundry.utils.duplicate(addDeadOptions);
    delete AddWoundedOptions["none"];
    const rollNPCSavesOptionsLocalized = foundry.utils.duplicate(rollNPCSavesOptions);
    for (let key of Object.keys(rollNPCSavesOptions)) {
      const notInstalled = game.i18n?.format("MODMANAGE.DepNotInstalled", { missing: "" }) ?? "";
      switch (key) {
        case "mtb":
          if (!installedModules.get("monks-tokenbar")) rollNPCSavesOptionsLocalized[key] = withOptionLabel(rollNPCSavesOptions[key], `${i18n(rollNPCSavesOptions[key])} - ${notInstalled}`);
          break;
        case "ftb":
          if (!installedModules.get("flash-rolls-5e")) rollNPCSavesOptionsLocalized[key] = withOptionLabel(rollNPCSavesOptions[key], `${i18n(rollNPCSavesOptions[key])} - ${notInstalled}`);
          break;
        case "rer":
          if (!installedModules.get("epic-rolls-5e")) rollNPCSavesOptionsLocalized[key] = withOptionLabel(rollNPCSavesOptions[key], `${i18n(rollNPCSavesOptions[key])} - ${notInstalled}`);
      }
    }
    let playerRollSavesOptionsLocalized = foundry.utils.duplicate(playerRollSavesOptions);
    for (let key of Object.keys(playerRollSavesOptions)) {
      const notInstalled = game.i18n?.format("MODMANAGE.DepNotInstalled", { missing: "" }) ?? "";
      switch (key) {
        case "mtb":
          if (!installedModules.get("monks-tokenbar")) playerRollSavesOptionsLocalized[key] = withOptionLabel(playerRollSavesOptionsLocalized[key], `${i18n(playerRollSavesOptionsLocalized[key])} - ${notInstalled}`);
          break;
        case "ftb":
          if (!installedModules.get("flash-rolls-5e")) playerRollSavesOptionsLocalized[key] = withOptionLabel(playerRollSavesOptionsLocalized[key], `${i18n(playerRollSavesOptionsLocalized[key])} - ${notInstalled}`);
          break;
        case "rer":
          if (!installedModules.get("epic-rolls-5e")) playerRollSavesOptionsLocalized[key] = withOptionLabel(playerRollSavesOptionsLocalized[key], `${i18n(playerRollSavesOptionsLocalized[key])} - ${notInstalled}`);
      }
    }
    ;
    const checkFlankingOptionsLocalized = foundry.utils.duplicate(checkFlankingOptions);
    const autoCEEffectsOptionsLocalized = foundry.utils.duplicate(autoCEEffectsOptions);
    if (!installedModules.get("dfreds-convenient-effects")) {
      const notActive = game.i18n?.localize("midi-qol.Inactive") ?? "Inactive";
      const ceFlankingKeys = ["ceonly", "ceflanked", "ceflankedNoconga"];
      for (const key of ceFlankingKeys) {
        if (key in checkFlankingOptionsLocalized) {
          checkFlankingOptionsLocalized[key] = withOptionLabel(checkFlankingOptions[key], `${i18n(checkFlankingOptions[key])} - ${notActive}`);
        }
      }
      const ceEffectKeys = ["itempri", "cepri", "both"];
      for (const key of ceEffectKeys) {
        if (key in autoCEEffectsOptionsLocalized) {
          autoCEEffectsOptionsLocalized[key] = withOptionLabel(autoCEEffectsOptions[key], `${i18n(autoCEEffectsOptions[key])} - ${notActive}`);
        }
      }
    }
    let statusEffectList = CONFIG.statusEffects.map((se) => {
      let name = i18n(se.name);
      if (se.id.startsWith("Convenient Effect")) name = `${name} (CE)`;
      return { id: se.id, name };
    });
    if (ceInterface) {
      statusEffectList = statusEffectList.concat(ceInterface.findEffects().map((ae) => ({ id: `z${ae.flags["dfreds-convenient-effects"].ceEffectId}`, name: `${ae.name} (CE)` })));
    }
    let StatusEffectOptions = statusEffectList.reduce((acc, { id, name }) => {
      acc[id] = name;
      return acc;
    }, { "none": "None" });
    const vitalityDeathRequiresOptions = {
      "vitalityOnly": "midi-qol.VitalityDeathRequires.VitalityOnly",
      "vitalityAndHP": "midi-qol.VitalityDeathRequires.VitalityAndHP"
    };
    let context = {
      configSettings,
      quickSettings: true,
      quickSettingsOptions,
      autoCheckHitOptions,
      autoTargetOptions: autoTargetOptionsLocalized,
      rangeTargetOptions,
      requiresTargetsOptions,
      autoCheckSavesOptions,
      autoRollDamageOptions,
      removeButtonsOptions,
      criticalDamage,
      criticalDamageChoices,
      autoApplyDamageOptions,
      damageCardStyleOptions,
      playerDamageCardOptions,
      damageImmunitiesOptions,
      showItemDetailsOptions,
      doReactionsOptions,
      wallsBlockRangeOptions,
      gmDoReactionsOptions,
      AutoCEEffectsOptions: autoCEEffectsOptionsLocalized,
      rollOtherDamageOptions,
      showReactionAttackRollOptions,
      CoverCalculationOptions,
      distanceMeasurementOptions,
      RecordAOOOptions: recordAOOOptions,
      EnforceReactionsOptions,
      AutoEffectsOptions: autoEffectsOptions,
      RequireMagicalOptions: requireMagicalOptions,
      itemTypeLabels: Object.keys(CONFIG.Item.typeLabels).filter((key) => !["backpack", "base"].includes(key)).reduce((acc, key) => {
        acc[key] = CONFIG.Item.typeLabels[key];
        return acc;
      }, {}),
      hasConvenientEffects: installedModules.get("dfreds-convenient-effects"),
      hideRollDetailsOptions,
      gmAttackDisplayOptions,
      gmDamageDisplayOptions,
      gmDsnDisplayOptions,
      checkFlankingOptions: checkFlankingOptionsLocalized,
      flankingBonusOptions,
      nsaFlag,
      coloredBorders,
      coloredBordersOptions,
      playerRollSavesOptions: playerRollSavesOptionsLocalized,
      rollNPCSavesOptions: rollNPCSavesOptionsLocalized,
      customSoundsPlaylistOptions: game.playlists?.contents.reduce((acc, e) => {
        acc[e.id] = e.name;
        return acc;
      }, {}) || {},
      customSoundOptions: game.playlists?.get(configSettings.customSoundsPlaylist)?.sounds.reduce((acc, s) => {
        acc[s.id ?? ""] = s.name;
        return acc;
      }, { "none": "" }),
      rollSoundOptions: CONFIG.sounds,
      isBetterRolls: installedModules.get("betterrolls5e"),
      rollAlternateOptions,
      ConsumeResourceOptions: consumeResourceOptions,
      AddDeadOptions: addDeadOptions,
      AddWoundedOptions,
      AverageDamageOptions: averageDamageOptions,
      RemoveConcentrationEffectsOptions: removeConcentrationEffectsOptions,
      IncapacitatedOptions: incapacitatedOptions,
      CheckRangeOptions: checkRangeOptions,
      InvisAdvantageOptions: invisAdvantageOptions,
      HiddenAdvantageOptions,
      ConfirmAttackDamageOptions: confirmAttackDamageOptions,
      ChallengeModeArmorOptions: challengeModeArmorOptions,
      RollSkillsBlindOptions: foundry.utils.mergeObject({ "all": "All" }, Object.keys(GameSystemConfig.skills).reduce((acc, s) => {
        acc[s] = GameSystemConfig.skills[s].label;
        return acc;
      }, {})),
      RollSavesBlindOptions: foundry.utils.mergeObject({ "all": "All", "death": i18n("DND5E.DeathSave") }, Object.keys(GameSystemConfig.abilities).reduce((acc, s) => {
        acc[s] = GameSystemConfig.abilities[s].label;
        return acc;
      }, {})),
      RollChecksBlindOptions: foundry.utils.mergeObject({ "all": "All" }, Object.keys(GameSystemConfig.abilities).reduce((acc, s) => {
        acc[s] = GameSystemConfig.abilities[s].label;
        return acc;
      }, {})),
      midiPropertiesTabOptions: CONST.USER_ROLE_NAMES,
      templateSnappingOptions: {
        "full": i18n("midi-qol.TemplateSnapping.Full"),
        "half": i18n("midi-qol.TemplateSnapping.Half")
      },
      StatusEffectOptions,
      vitalityDeathRequiresOptions,
      SaveDROrderOptions: saveDROrderOptions,
      ColorOptions: colorList.reduce((acc, c) => {
        acc[c] = c;
        return acc;
      }, { "Delete": "Delete" }),
      DoConcentrationCheckOptions: doConcentrationCheckOptions,
      AddChatDamageButtonsOptions: addChatDamageButtonsOptions,
      ActivationAutomationOptions: activationAutomationOptions,
      rollModes: CONFIG.Dice.rollModes,
      fastForwardOptions: {
        attack: i18n("midi-qol.fastForwardOptions.attack"),
        damage: i18n("midi-qol.fastForwardOptions.damage"),
        check: i18n("midi-qol.fastForwardOptions.check"),
        save: i18n("midi-qol.fastForwardOptions.save"),
        skill: i18n("midi-qol.fastForwardOptions.skill"),
        tool: i18n("midi-qol.fastForwardOptions.tool")
      },
      preferredGMOptions: game.users?.filter((user) => user.isGM).reduce((acc, user) => {
        acc[user.id] = user.name ?? "";
        return acc;
      }, { "": i18n("None") })
    };
    context = foundry.utils.mergeObject(await super._prepareContext(options), context, { inplace: false });
    context.concentrationDisabled = safeGetGameSetting("dnd5e", "disableConcentration");
    const attackRollVisibility = safeGetGameSetting("dnd5e", "attackRollVisibility");
    if (attackRollVisibility !== void 0) {
      const choices = game.settings.settings.get("dnd5e.attackRollVisibility")?.choices;
      context.attackRollVisibilityOptions = choices;
      context.attackRollVisibilityValue = attackRollVisibility;
    }
    const challengeVisibility = safeGetGameSetting("dnd5e", "challengeVisibility");
    if (challengeVisibility !== void 0) {
      const choices = game.settings.settings.get("dnd5e.challengeVisibility")?.choices;
      context.challengeVisibilityOptions = choices;
      context.challengeVisibilityValue = challengeVisibility;
    }
    if (debugEnabled > 0) warn("Config Panel: getData ", context);
    return context;
  }
  changeTab(tab, group, options) {
    super.changeTab(tab, group, options);
    this.activeTab = tab;
  }
  // V12-only (I think): does not seem to be
  async _preparePartContext(partId, context) {
    const partContext = await super._preparePartContext(partId, context);
    if (partId in (partContext.tabs ?? {})) partContext.tab = partContext.tabs[partId];
    return partContext;
  }
  _onSearch(term) {
    for (let tag of [".midi-qol-box", ".form-group"]) {
      const elts = Array.from(this.element.querySelectorAll(tag));
      term = term.toLowerCase().trim();
      elts.forEach((el) => {
        if (!term || el.innerText.toLowerCase().includes(term)) {
          el.style.display = null;
        } else {
          el.style.display = "none";
        }
      });
    }
  }
  async _onRender(context, options) {
    super._onRender(context, options);
    this.element.querySelector(".itemTypeListEdit")?.addEventListener("click", (event) => {
      const options2 = Object.entries(CONFIG.Item.typeLabels).filter((kv) => !["backpack", "base"].includes(kv[0])).map(([k, v]) => ({ value: k, label: i18n(v), selected: configSettings.itemTypeList?.includes(k) }));
      options2.sort((a, b) => a.label.compare(b.label));
      DialogV22.prompt({
        id: "midi-qol-item-selector",
        content: foundry.applications.fields.createMultiSelectInput({
          type: "checkboxes",
          name: "items",
          options: options2
        }).outerHTML,
        window: { title: "Show Item Details" },
        position: {
          height: "auto"
        },
        ok: {
          label: "DND5E.TraitSave",
          icon: "fa-solid fa-floppy-disk",
          callback: /* @__PURE__ */ __name((evt, button) => {
            const data = new foundry.applications.ux.FormDataExtended(button.form);
            if (data) configSettings.itemTypeList = data.object?.items ?? configSettings.itemTypeList;
          }, "callback")
        },
        rejectClose: false
      });
    });
    this.element.querySelector(".optionalRulesEnabled")?.addEventListener("click", (event) => {
      configSettings.optionalRulesEnabled = !configSettings.optionalRulesEnabled;
      this.render({ force: true });
    });
    this.element.querySelector("#midi-qol-export-config")?.addEventListener("click", exportSettingsToJSON);
    this.element.querySelector("#midi-qol-import-config")?.addEventListener("click", async () => {
      if (await importFromJSONDialog()) this.close();
    });
    for (const elem of Array.from(this.element.querySelectorAll(".midi-qol-blind-select"))) {
      elem.addEventListener("mouseenter", this.selectHover.bind(this));
      elem.addEventListener("mouseleave", this.selectHoverOut.bind(this));
    }
    for (const elem of Array.from(this.element.querySelectorAll(".import-quick-setting"))) {
      elem.addEventListener("click", async function(event) {
        const key = event.currentTarget?.id;
        if (await applySettings.bind(this)(key)) this.close();
      }.bind(this));
    }
    this.element.querySelector('input[type="search"]')?.addEventListener("input", (e) => {
      this._onSearch(e.currentTarget?.value);
    });
  }
  selectHover(event) {
    const target = event.currentTarget;
    target.focus();
  }
  selectHoverOut(event) {
    const target = event.currentTarget;
    target.blur();
  }
  async _preClose(options) {
    await super._preClose(options);
    if (this.configHookId) Hooks.off("midi-qol.ConfigSettingsChanged", this.configHookId);
  }
  // async _playList(event) {
  //   event.preventDefault();
  //   configSettings.customSoundsPlaylist = `${$(event.currentTarget).children("option:selected").val()}`;
  //   await this.submit({ preventClose: true });
  //   this.render();
  // }
  // onReset() {
  //   this.render(true);
  // }
  static async #onSubmit(event, form, formData) {
    const realData = formData.object;
    let needReload = false;
    realData.itemTypeList = configSettings.itemTypeList;
    if (realData.enforceReactions !== "none" && configSettings.enforceReactions !== realData.enforceReactions) {
      needReload = true;
    }
    if (realData.enforceBonusActions !== "none" && configSettings.enforceBonusActions !== realData.enforceBonusActions) {
      needReload = true;
    }
    if (configSettings.activityNamePrefix !== realData.activityNamePrefix) needReload = true;
    if (realData.dnd5eAttackRollVisibility !== void 0) {
      await game.settings.set("dnd5e", "attackRollVisibility", realData.dnd5eAttackRollVisibility);
      delete realData.dnd5eAttackRollVisibility;
    }
    if (realData.dnd5eChallengeVisibility !== void 0) {
      await game.settings.set("dnd5e", "challengeVisibility", realData.dnd5eChallengeVisibility);
      delete realData.dnd5eChallengeVisibility;
    }
    let newSettings = foundry.utils.mergeObject(configSettings, realData, { overwrite: true, inplace: false });
    if (game.user?.can("SETTINGS_MODIFY")) {
      await game.settings.set("midi-qol", "ConfigSettings", newSettings);
      if (needReload) foundry.applications.settings.SettingsConfig.reloadConfirm({ world: true });
    }
  }
};
async function importFromJSONDialog() {
  const content = await foundry.applications.handlebars.renderTemplate("templates/apps/import-data.hbs", { entity: "midi-qol", name: "settings" });
  let dialog = new Promise((resolve, reject) => {
    new DialogV22({
      window: { title: `Import midi-qol settings` },
      position: {
        width: 400,
        height: "auto"
      },
      content,
      buttons: [
        {
          action: "import",
          icon: "fa-solid fa-file-import",
          label: "Import",
          default: true,
          callback: /* @__PURE__ */ __name((event) => {
            const form = event.currentTarget?.querySelector("form");
            if (!form?.data.files.length) return ui.notifications?.error("You did not upload a data file!");
            foundry.utils.readTextFromFile(form.data.files[0]).then((json) => {
              importSettingsFromJSON(json).then(() => resolve(true));
            });
          }, "callback")
        },
        {
          action: "no",
          icon: "fa-solid fa-xmark",
          label: "Cancel",
          callback: /* @__PURE__ */ __name((event) => resolve(false), "callback")
        }
      ]
    }).render({ force: true });
  });
  return await dialog;
}
__name(importFromJSONDialog, "importFromJSONDialog");
async function fetchConfigFile(filename) {
  if (!filename) return "{}";
  return new Promise((resolve, reject) => {
    fetch(filename).then((response) => response.text()).then((data) => {
      resolve(data);
    });
  });
}
__name(fetchConfigFile, "fetchConfigFile");
function showDiffs(current, changed, flavor = "", title = "") {
  const diffs = foundry.utils.diffObject(changed, current, { inner: true });
  const changes = [];
  for (let key of Object.keys(diffs)) {
    let name;
    if (key.startsWith("gm"))
      name = key[2].toUpperCase() + key.substring(3);
    else name = key[0].toUpperCase() + key.substring(1);
    let longName = i18n("midi-qol." + name + ".Name");
    if (longName?.startsWith("midi-qol")) longName = name;
    debug("Show config changes: Name is ", name, key, key.startsWith("gm") ? "GM" : "", i18n(`midi-qol.${name + ".Name"}`));
    let currentVal = current[key];
    let changedVal = changed[key];
    if (typeof currentVal === "object") currentVal = "Object";
    if (typeof changedVal === "object") changedVal = "Object";
    changes.push(`${key.startsWith("gm") ? "GM " : ""}${longName} <strong>${currentVal} => ${changedVal}</strong>`);
  }
  if (changes.length === 0) changes.push("No Changes");
  const dialog = new Promise((resolve, reject) => {
    let dialogTitle;
    if (title !== "") dialogTitle = `${i18n("midi-qol.Config.Tabs.quick")} - ${title}`;
    else dialogTitle = i18n("midi-qol.Config.Tabs.quick");
    let d = new DialogV22({
      classes: ["midi-qol-quick-config"],
      window: { title: dialogTitle },
      position: {
        height: "auto"
      },
      content: changes.join("<br>"),
      buttons: [
        {
          action: "apply",
          default: true,
          icon: "fa-solid fa-check",
          label: "Apply",
          callback: /* @__PURE__ */ __name(() => resolve(true), "callback")
        },
        {
          action: "abort",
          icon: "fa-solid fa-xmark",
          label: "Don't Apply",
          callback: /* @__PURE__ */ __name(() => resolve(false), "callback")
        }
      ]
    }).render({ force: true });
    warn("Quick Settings ", changes.join("\n"));
  });
  return dialog;
}
__name(showDiffs, "showDiffs");
var quickSettingsDetails = {
  FullAuto: {
    description: "Full Automation: As few button presses as possible",
    shortDescription: "Full Automation",
    fileName: "midi-qol-full-auto.json"
  },
  FullManual: {
    description: "No Automation: All rolls manual",
    shortDescription: "No Automation",
    fileName: "midi-qol-manual.json"
  },
  GMAuto: {
    description: "GM Attack/Damage: Automatic",
    shortDescription: "GM Attack/Damage: Automatic",
    configSettings: {
      gmAutoAttack: true,
      gmAutoDamage: "onHit",
      gmAutoFastForward: ["attack", "damage"],
      gmRemoveButtons: "all",
      gmTargetConfirmation: "none",
      autoItemEffects: "applyRemove",
      allowUseMacro: true
    }
  },
  GMManual: {
    description: "GM Attack/Damage: Manual",
    shortDescription: "GM Attack/Damage: Manual",
    configSettings: {
      gmAutoAttack: false,
      gmAutoDamage: "none",
      gmAutoFastForward: [],
      gmRemoveButtons: "none",
      gmTargetConfirmation: "none"
    }
  },
  ShowItemInfo: {
    description: "Show Item Info in chat card",
    shortDescription: "Show Item Info",
    configSettings: {
      showItemDetails: "all"
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      settings.itemTypeList = Object.keys(CONFIG.Item.typeLabels).filter((key) => !["backpack", "base"].includes(key));
    }, "codeChecks")
  },
  PlayerAuto: {
    description: "Player Attack/Damage Roll: Automatic",
    shortDescription: "Player Attack/Damage Roll: Automatic",
    configSettings: {
      autoRollAttack: true,
      autoRollDamage: "onHit",
      autoFastForward: ["attack", "damage"],
      removeButtons: "all",
      targetConfirmation: "none"
    }
  },
  PlayerManual: {
    description: "Player Attack/Damage Roll: Manual",
    shortDescription: "Player Attack/Damage Roll: Manual",
    configSettings: {
      autoRollAttack: false,
      autoRollDamage: "none",
      autoFastForward: [],
      removeButtons: "none",
      targetConfirmation: "none"
    }
  },
  DamageAuto: {
    description: "Automatic Hits/Saves/damage application",
    shortDescription: "Auto. Hits/Saves/dmg. application",
    configSettings: {
      autoCheckHit: "all",
      autoCheckSaves: "all",
      removeButtons: "all",
      playerRollSaves: "chat",
      playerSaveTimeout: 30,
      rollNPCSaves: "auto",
      autoTarget: "wallsBlockIgnoreDefeated",
      rangeTarget: "alwaysIgnoreDefeated",
      rollNPCLinkedSaves: "auto",
      autoCEEffects: "cepri",
      autoItemEffects: "applyRemove",
      allowUseMacro: true,
      autoApplyDamage: "yesCard"
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
    }, "codeChecks")
  },
  DamageManual: {
    description: "No Hits/Saves/damage application automation",
    shortDescription: "No Hits/Saves/dmg. app. automation",
    configSettings: {
      autoCheckHit: "none",
      autoCheckSaves: "none",
      playerRollSaves: "chat",
      playerSaveTimeout: 30,
      rollNPCSaves: "chat",
      autoTarget: "wallsBlockIgnoreDefeated",
      rangeTarget: "alwaysIgnoreDefeated",
      rollNPCLinkedSaves: "chat",
      autoCEEffects: "cepri",
      autoItemEffects: "off",
      allowUseMacro: true,
      autoApplyDamage: "no"
    }
  },
  EnableReactions: {
    description: "Turn on Reaction processing",
    shortDescription: "Turn on Reaction processing",
    configSettings: {
      "doReactions": "all",
      "gmDoReactions": "all",
      "reactionTimeout": 30,
      "showReactionAttackRoll": "all",
      enforceReactions: "all",
      recordAOO: "all"
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      let changesMade = false;
      if (current.autoCheckHit === "none") {
        settings.autoCheckHit = "whisper";
        changesMade = true;
      }
      if (current.autoCheckSaves === "none") {
        settings.autoCheckSaves = "whisper";
        changesMade = true;
      }
      if (current.playerRollSaves === "none") {
        settings.playerRollSaves = "chat";
        changesMade = true;
      }
      if (current.rollNPCLinkedSaves === "none") {
        settings.rollNPCLinkedSaves = "chat";
        changesMade = true;
      }
      if (current.autoApplyDamage === "none") {
        settings.autoApplyDamage = "noCard";
        changesMade = true;
      }
      if (changesMade) ui.notifications?.warn("midi-qol Some automation enabled to support reaction processing");
    }, "codeChecks")
  },
  DisableReactions: {
    description: "Turn off Reaction processing",
    shortDescription: "Turn off Reaction processing",
    configSettings: {
      doReactions: "none",
      gmDoReactions: "none",
      reactionTimeout: 0,
      showReactionAttackRoll: "all",
      enforceReactions: "none",
      recordAOO: "none"
    }
  },
  EnableConcentration: {
    description: "Enable Concentration Automation",
    shortDescription: "Enable Concentration Automation",
    configSettings: {
      removeConcentration: true,
      singleConcentrationRoll: true
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      game.settings.set(game.system?.id, "disableConcentration", false);
    }, "codeChecks")
  },
  NoDamageApplication: {
    description: "Allow GM to fudge damage application (display but no auto apply)",
    shortDescription: "Allow GM to fudge damage application",
    configSettings: {
      autoApplyDamage: "noCard"
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      game.settings.set("midi-qol", "AddChatDamageButtons", "gm");
    }, "codeChecks")
  },
  DisableConcentration: {
    description: "Disable Concentration Automation",
    shortDescription: "Disable Concentration Automation",
    configSettings: {
      removeConcentration: false,
      singleConcentrationRoll: false
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      game.settings.set(game.system?.id, "disableConcentration", true);
    }, "codeChecks")
  },
  SecretSquirrel: {
    description: "Secret Squirrel: Hide most GM roll info from players",
    shortDescription: "Secret Squirrel",
    configSettings: {
      gmAttackDisplay: "hidden",
      gmDamageDisplay: "hidden",
      gmAttackDsn: "none",
      gmDamageDsn: "none",
      displaySaveAdvantage: false,
      hideNPCNames: "Unknown Creature",
      showReactionAttackRoll: "none",
      displayHitResultNumeric: false
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      if (current.autoCheckHit !== "none") settings.autoCheckHit = "whisper";
      if (current.autoCheckSaves !== "none") settings.autoCheckSaves = "whisper";
      if (!installedModules.get("anonymous")) ui.notifications?.warn("'Anonymous' is recommended to hide creature names for normal dnd5e rolls");
      game.settings.set("dnd5e", "challengeVisibility", "none");
    }, "codeChecks")
  },
  FullDisclosure: {
    description: "Full Disclosure: Players see the details of all GM rolls and the results",
    shortDescription: "Full Disclosure",
    configSettings: {
      gmAttackDisplay: "full",
      gmDamageDisplay: "full",
      gmAttackDsn: "show",
      gmDamageDsn: "show",
      displaySaveAdvantage: true,
      showReactionAttackRoll: "all",
      hideNPCNames: "",
      displayHitResultNumeric: true
    },
    codeChecks: /* @__PURE__ */ __name((current, settings) => {
      if (current.autoCheckHit !== "none") settings.autoCheckHit = "all";
      if (current.autoCheckSaves !== "none") settings.autoCheckSaves = "allShow";
      game.settings.set("dnd5e", "challengeVisibility", "all");
    }, "codeChecks")
  }
};
async function applySettings(key) {
  let settingsToApply = {};
  const config = quickSettingsDetails[key];
  if (config.configSettings) {
    settingsToApply = foundry.utils.duplicate(config.configSettings);
    if (config.codeChecks) config.codeChecks(configSettings, settingsToApply);
    if (await showDiffs(configSettings, settingsToApply, "", config.shortDescription)) {
      settingsToApply = foundry.utils.mergeObject(configSettings, settingsToApply, { overwrite: true, inplace: true });
      if (game.user?.can("SETTINGS_MODIFY")) game.settings.set("midi-qol", "ConfigSettings", settingsToApply);
      return true;
    }
  } else if (config.fileName) {
    try {
      const jsonText = await fetchConfigFile(PATH + config.fileName);
      const configData = JSON.parse(jsonText);
      if (await showDiffs(configSettings, configData.configSettings, "", config.shortDescription)) {
        await importSettingsFromJSON(jsonText);
        log(`Loaded ${config.fileName}`);
      }
      return true;
    } catch (err) {
      error2("could not load config file", config.fileName, err);
    }
  }
  return false;
}
__name(applySettings, "applySettings");
var colorList = [
  `AliceBlue`,
  `AntiqueWhite`,
  `Aqua`,
  `Aquamarine`,
  `Azure`,
  `Beige`,
  `Bisque`,
  `Black`,
  `BlanchedAlmond`,
  `Blue`,
  `BlueViolet`,
  `Brown`,
  `BurlyWood`,
  `CadetBlue`,
  `Chartreuse`,
  `Chocolate`,
  `Coral`,
  `CornflowerBlue`,
  `Cornsilk`,
  `Crimson`,
  `Cyan`,
  `DarkBlue`,
  `DarkCyan`,
  `DarkGoldenRod`,
  `DarkGray`,
  `DarkGrey`,
  `DarkGreen`,
  `DarkKhaki`,
  `DarkMagenta`,
  `DarkOliveGreen`,
  `Darkorange`,
  `DarkOrchid`,
  `DarkRed`,
  `DarkSalmon`,
  `DarkSeaGreen`,
  `DarkSlateBlue`,
  `DarkSlateGray`,
  `DarkSlateGrey`,
  `DarkTurquoise`,
  `DarkViolet`,
  `DeepPink`,
  `DeepSkyBlue`,
  `DimGray`,
  `DimGrey`,
  `DodgerBlue`,
  `FireBrick`,
  `FloralWhite`,
  `ForestGreen`,
  `Fuchsia`,
  `Gainsboro`,
  `GhostWhite`,
  `Gold`,
  `GoldenRod`,
  `Gray`,
  `Grey`,
  `Green`,
  `GreenYellow`,
  `HoneyDew`,
  `HotPink`,
  `IndianRed`,
  `Indigo`,
  `Ivory`,
  `Khaki`,
  `Lavender`,
  `LavenderBlush`,
  `LawnGreen`,
  `LemonChiffon`,
  `LightBlue`,
  `LightCoral`,
  `LightCyan`,
  `LightGoldenRodYellow`,
  `LightGray`,
  `LightGrey`,
  `LightGreen`,
  `LightPink`,
  `LightSalmon`,
  `LightSeaGreen`,
  `LightSkyBlue`,
  `LightSlateGray`,
  `LightSlateGrey`,
  `LightSteelBlue`,
  `LightYellow`,
  `Lime`,
  `LimeGreen`,
  `Linen`,
  `Magenta`,
  `Maroon`,
  `MediumAquaMarine`,
  `MediumBlue`,
  `MediumOrchid`,
  `MediumPurple`,
  `MediumSeaGreen`,
  `MediumSlateBlue`,
  `MediumSpringGreen`,
  `MediumTurquoise`,
  `MediumVioletRed`,
  `MidnightBlue`,
  `MintCream`,
  `MistyRose`,
  `Moccasin`,
  `NavajoWhite`,
  `Navy`,
  `OldLace`,
  `Olive`,
  `OliveDrab`,
  `Orange`,
  `OrangeRed`,
  `Orchid`,
  `PaleGoldenRod`,
  `PaleGreen`,
  `PaleTurquoise`,
  `PaleVioletRed`,
  `PapayaWhip`,
  `PeachPuff`,
  `Peru`,
  `Pink`,
  `Plum`,
  `PowderBlue`,
  `Purple`,
  `Red`,
  `RosyBrown`,
  `RoyalBlue`,
  `SaddleBrown`,
  `Salmon`,
  `SandyBrown`,
  `SeaGreen`,
  `SeaShell`,
  `Sienna`,
  `Silver`,
  `SkyBlue`,
  `SlateBlue`,
  `SlateGray`,
  `SlateGrey`,
  `Snow`,
  `SpringGreen`,
  `SteelBlue`,
  `Tan`,
  `Teal`,
  `Thistle`,
  `Tomato`,
  `Turquoise`,
  `Violet`,
  `Wheat`,
  `White`,
  `WhiteSmoke`,
  `Yellow`,
  `YellowGreen`
];

// src/module/lib/MidiDependentsRegistry.ts
var MidiDependentsRegistry = class {
  static {
    __name(this, "MidiDependentsRegistry");
  }
  // Map of parent document UUID -> Set of dependent document UUIDs
  static #dependents = /* @__PURE__ */ new Map();
  /**
   * Get all dependent documents for a given parent document.
   * @param document The parent document (Actor, Item, Effect, etc.) or its UUID
   * @returns Array of dependent documents
   */
  static get(document2) {
    const uuid = typeof document2 === "string" ? document2 : document2?.uuid;
    if (!uuid) return [];
    return Array.from(this.#dependents.get(uuid) ?? []).map((depUuid) => fromUuidSync(depUuid, { strict: false })).filter((d) => !!d);
  }
  /**
   * Track a document as dependent on a parent document.
   * @param parentUuid UUID of the parent document
   * @param dependent The dependent document
   */
  static track(parentUuid, dependent) {
    if (!parentUuid || !dependent?.uuid) return;
    const isNewParent = !this.#dependents.has(parentUuid);
    if (isNewParent) this.#dependents.set(parentUuid, /* @__PURE__ */ new Set());
    const dependentSet = this.#dependents.get(parentUuid);
    const isNewDependent = !dependentSet.has(dependent.uuid);
    if (!isNewDependent) return;
    dependentSet.add(dependent.uuid);
    if (debugEnabled > 0) log(`MidiDependentsRegistry | TRACK: ${"name" in dependent ? dependent.name : ""} (${dependent.uuid}) depends on ${parentUuid}`);
    Hooks.callAll("midi-qol.dependentsRegistryChanged", { type: "track", parentUuid, dependentUuid: dependent.uuid });
  }
  /**
   * Stop tracking a document as dependent.
   * @param parentUuid UUID of the parent document
   * @param dependent The dependent document to stop tracking
   */
  static untrack(parentUuid, dependent) {
    if (!parentUuid || !dependent?.uuid) return;
    const dependentSet = this.#dependents.get(parentUuid);
    if (!dependentSet?.has(dependent.uuid)) return;
    dependentSet.delete(dependent.uuid);
    if (dependentSet.size === 0) this.#dependents.delete(parentUuid);
    if (debugEnabled > 0) log(`MidiDependentsRegistry | UNTRACK: ${"name" in dependent ? dependent.name : ""} (${dependent.uuid}) no longer depends on ${parentUuid}`);
    Hooks.callAll("midi-qol.dependentsRegistryChanged", { type: "untrack", parentUuid, dependentUuid: dependent.uuid });
  }
  /**
   * Delete all dependents of a parent document.
   * Called when the parent document is deleted.
   * @param parentUuid UUID of the deleted parent document
   */
  static async deleteDependents(parentUuid) {
    const dependents = this.get(parentUuid);
    if (dependents.length === 0) return;
    if (debugEnabled > 0) log(`MidiDependentsRegistry | Deleting ${dependents.length} dependents of ${parentUuid}`);
    for (const dependent of dependents) {
      try {
        await dependent.delete({});
      } catch (err) {
        if (debugEnabled > 0) warn(`MidiDependentsRegistry | Could not delete dependent ${dependent.uuid}:`, err);
      }
    }
    this.#dependents.delete(parentUuid);
    Hooks.callAll("midi-qol.dependentsRegistryChanged", { type: "deleteDependents", parentUuid });
  }
  /**
   * Clear all entries from the registry.
   * Useful for cleanup on world/scene change.
   */
  static clear() {
    if (this.#dependents.size === 0) return;
    this.#dependents.clear();
    Hooks.callAll("midi-qol.dependentsRegistryChanged", { type: "clear" });
  }
  /**
   * Get all parent-dependent relationships in the registry.
   * @returns Array of {parentUuid, dependentUuids} objects
   */
  static getAll() {
    const entries = [];
    for (const [parentUuid, dependentSet] of this.#dependents.entries()) {
      if (dependentSet.size > 0) {
        entries.push({ parentUuid, dependentUuids: Array.from(dependentSet) });
      }
    }
    return entries;
  }
  /**
   * Check if registry has any entries.
   */
  static get size() {
    return this.#dependents.size;
  }
};
function MidiDependentDocumentMixin(Base) {
  class MidiDependentDocument extends Base {
    static {
      __name(this, "MidiDependentDocument");
    }
    /** @inheritDoc */
    prepareData() {
      super.prepareData();
      if (game.ready) return;
      try {
        const dependentOn = this.flags?.dnd5e?.dependentOn;
        if (dependentOn && this.uuid) {
          MidiDependentsRegistry.track(dependentOn, this);
        }
      } catch (err) {
        const message = `MidiDependentsRegistry.prepareData`;
        TroubleShooter.recordError(err, message);
        error2(message, err);
      }
    }
    /** @inheritDoc */
    _onCreate(data, options, userId) {
      const returnValue = super._onCreate(data, options, userId);
      try {
        const dependentOn = this.flags?.dnd5e?.dependentOn;
        if (dependentOn && this.uuid) {
          MidiDependentsRegistry.track(dependentOn, this);
        }
      } catch (err) {
        const message = `MidiDependentsRegistry._onCreate`;
        TroubleShooter.recordError(err, message);
        error2(message, err);
      }
      return returnValue;
    }
    /** @inheritDoc */
    async _preUpdate(changed, options, user) {
      const allowed = await super._preUpdate(changed, options, user);
      try {
        const dnd5eFlags = changed.flags?.dnd5e;
        if (!dnd5eFlags) return allowed;
        if (!("dependentOn" in dnd5eFlags || "-=dependentOn" in dnd5eFlags)) return allowed;
        const oldParent = this.flags?.dnd5e?.dependentOn;
        if (oldParent && this.uuid) {
          MidiDependentsRegistry.untrack(oldParent, this);
        }
      } catch (err) {
        const message = `MidiDependentsRegistry._preUpdate`;
        TroubleShooter.recordError(err, message);
        error2(message, err);
      }
      return allowed;
    }
    /** @inheritDoc */
    _onUpdate(changed, options, userId) {
      super._onUpdate(changed, options, userId);
      try {
        const dnd5eFlags = changed.flags?.dnd5e;
        if (!dnd5eFlags || !("dependentOn" in dnd5eFlags)) return;
        const newParent = this.flags?.dnd5e?.dependentOn;
        if (newParent && this.uuid) {
          MidiDependentsRegistry.track(newParent, this);
        }
      } catch (err) {
        const message = `MidiDependentsRegistry._onUpdate`;
        TroubleShooter.recordError(err, message);
        error2(message, err);
      }
    }
    /** @inheritDoc */
    _onDelete(options, userId) {
      super._onDelete(options, userId);
      try {
        const dependentOn = this.flags?.dnd5e?.dependentOn;
        if (dependentOn && this.uuid) {
          MidiDependentsRegistry.untrack(dependentOn, this);
        }
      } catch (err) {
        const message = `MidiDependentsRegistry._onDelete`;
        TroubleShooter.recordError(err, message);
        error2(message, err);
      }
    }
  }
  return MidiDependentDocument;
}
__name(MidiDependentDocumentMixin, "MidiDependentDocumentMixin");

// src/module/apps/DependentsExplorer.ts
var { ApplicationV2: ApplicationV24, HandlebarsApplicationMixin: HandlebarsApplicationMixin4, DialogV2: DialogV23 } = foundry.applications.api;
var RENDER_THROTTLE_MS = 100;
var SEARCH_DEBOUNCE_MS = 300;
var DependentsExplorer = class extends HandlebarsApplicationMixin4(ApplicationV24) {
  static {
    __name(this, "DependentsExplorer");
  }
  #searchFilter = "";
  #collapsedSections = /* @__PURE__ */ new Set();
  #hookIds = /* @__PURE__ */ new Map();
  // Cache of processed entries by parent UUID for incremental updates
  #entriesCache = /* @__PURE__ */ new Map();
  #cacheValid = false;
  // Throttled render to batch rapid UI updates
  #throttledRender = foundry.utils.throttle(() => this.render({ force: true }), RENDER_THROTTLE_MS);
  // Debounced search render to avoid cursor issues
  #debouncedSearchRender = foundry.utils.debounce(() => this.#renderSearchResults(), SEARCH_DEBOUNCE_MS);
  static DEFAULT_OPTIONS = {
    id: "midi-qol-dependents-explorer",
    window: {
      title: "midi-qol.DependentsExplorer.Title",
      resizable: true,
      contentClasses: ["standard-form", "dependents-explorer"]
    },
    position: {
      width: 600,
      height: 500
    },
    actions: {
      deleteParent: this.#onDeleteParent,
      deleteDependent: this.#onDeleteDependent,
      openDocument: this.#onOpenDocument,
      toggleSection: this.#onToggleSection,
      clearFilter: this.#onClearFilter
    }
  };
  static PARTS = {
    main: {
      template: "modules/midi-qol/templates/apps/DependentsExplorer/dependentsExplorer.hbs",
      // Preserve scroll position during re-render
      scrollable: [".sections-list"]
    }
  };
  get title() {
    return i18n("midi-qol.DependentsExplorer.Title");
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    if (!this.#cacheValid) {
      this.#rebuildCache();
    }
    const entriesByType = /* @__PURE__ */ new Map();
    const filter = this.#searchFilter.toLowerCase();
    for (const entry of this.#entriesCache.values()) {
      if (filter && !entry.parentName.toLowerCase().includes(filter)) {
        const hasMatchingDependent = entry.dependents.some(
          (dep) => dep.name.toLowerCase().includes(filter)
        );
        if (!hasMatchingDependent) continue;
      }
      if (!entriesByType.has(entry.parentType)) {
        entriesByType.set(entry.parentType, []);
      }
      entriesByType.get(entry.parentType).push(entry);
    }
    const sections = [];
    for (const [type, entries] of entriesByType) {
      if (entries.length === 0) continue;
      entries.sort((a, b) => a.parentName.localeCompare(b.parentName));
      sections.push({
        type,
        label: this.#getTypeLabel(type),
        icon: this.#getTypeIcon(type),
        collapsed: this.#collapsedSections.has(type),
        entries,
        count: entries.length
      });
    }
    sections.sort((a, b) => a.label.localeCompare(b.label));
    context.sections = sections;
    context.isEmpty = sections.length === 0;
    context.emptyMessage = filter ? i18n("midi-qol.DependentsExplorer.NoMatches") : i18n("midi-qol.DependentsExplorer.Empty");
    context.searchFilter = this.#searchFilter;
    context.totalCount = this.#entriesCache.size;
    return context;
  }
  #getDocumentIcon(doc) {
    if (!doc) return "fas fa-question";
    return this.#getTypeIcon(doc.documentName);
  }
  #getDisplayType(type) {
    return type === "ActiveEffect" ? "Effect" : type;
  }
  #getTypeIcon(type) {
    switch (type) {
      case "Actor":
        return "fas fa-user";
      case "Item":
        return "fas fa-suitcase";
      case "ActiveEffect":
        return "fas fa-bolt";
      case "Token":
        return "fas fa-user-circle";
      case "Scene":
        return "fas fa-map";
      case "Combatant":
        return "fas fa-fist-raised";
      default:
        return "fas fa-file";
    }
  }
  #getTypeLabel(type) {
    switch (type) {
      case "Actor":
        return i18n("midi-qol.DependentsExplorer.TypeActor");
      case "Item":
        return i18n("midi-qol.DependentsExplorer.TypeItem");
      case "ActiveEffect":
        return i18n("midi-qol.DependentsExplorer.TypeActiveEffect");
      case "Token":
        return i18n("midi-qol.DependentsExplorer.TypeToken");
      case "Scene":
        return i18n("midi-qol.DependentsExplorer.TypeScene");
      case "Combatant":
        return i18n("midi-qol.DependentsExplorer.TypeCombatant");
      default:
        return type;
    }
  }
  async _onFirstRender(context, options) {
    await super._onFirstRender(context, options);
    this.#hookIds.set("registryChanged", Hooks.on("midi-qol.dependentsRegistryChanged", (event) => {
      this.#handleRegistryChange(event);
    }));
    this.#hookIds.set("updateItem", Hooks.on("updateItem", (doc) => {
      this.#handleDocumentUpdate(doc.uuid);
    }));
    this.#hookIds.set("updateActiveEffect", Hooks.on("updateActiveEffect", (doc) => {
      this.#handleDocumentUpdate(doc.uuid);
    }));
    this.#hookIds.set("updateActor", Hooks.on("updateActor", (doc) => {
      this.#handleDocumentUpdate(doc.uuid);
    }));
  }
  #handleDocumentUpdate(uuid) {
    if (!this.#cacheValid) return;
    if (!this.#isDocumentInCache(uuid)) return;
    this.#refreshDocumentInCache(uuid);
    this.#throttledRender();
  }
  #isDocumentInCache(uuid) {
    if (this.#entriesCache.has(uuid)) return true;
    for (const entry of this.#entriesCache.values()) {
      if (entry.dependents.some((d) => d.uuid === uuid)) return true;
    }
    return false;
  }
  #refreshDocumentInCache(uuid) {
    if (this.#entriesCache.has(uuid)) {
      const entry = this.#entriesCache.get(uuid);
      const dependentUuids = entry.dependents.map((d) => d.uuid);
      const newEntry = this.#buildParentEntry(uuid, dependentUuids);
      if (newEntry) {
        this.#entriesCache.set(uuid, newEntry);
      }
    }
    for (const [parentUuid, entry] of this.#entriesCache.entries()) {
      const depIndex = entry.dependents.findIndex((d) => d.uuid === uuid);
      if (depIndex !== -1) {
        const newDepInfo = this.#buildDependentInfo(uuid);
        if (newDepInfo) {
          entry.dependents[depIndex] = newDepInfo;
        }
      }
    }
  }
  #handleRegistryChange(event) {
    switch (event.type) {
      case "track":
        if (event.parentUuid && event.dependentUuid) {
          this.#addToCache(event.parentUuid, event.dependentUuid);
        }
        break;
      case "untrack":
        if (event.parentUuid && event.dependentUuid) {
          this.#removeFromCache(event.parentUuid, event.dependentUuid);
        }
        break;
      case "clear":
      case "deleteDependents":
        this.#cacheValid = false;
        break;
    }
    this.#throttledRender();
  }
  #addToCache(parentUuid, dependentUuid) {
    if (!this.#cacheValid) return;
    const existingEntry = this.#entriesCache.get(parentUuid);
    if (existingEntry) {
      if (!existingEntry.dependents.some((d) => d.uuid === dependentUuid)) {
        const depInfo = this.#buildDependentInfo(dependentUuid);
        if (depInfo) {
          existingEntry.dependents.push(depInfo);
        }
      }
    } else {
      const entry = this.#buildParentEntry(parentUuid, [dependentUuid]);
      if (entry) {
        this.#entriesCache.set(parentUuid, entry);
      }
    }
  }
  #removeFromCache(parentUuid, dependentUuid) {
    if (!this.#cacheValid) return;
    const existingEntry = this.#entriesCache.get(parentUuid);
    if (existingEntry) {
      existingEntry.dependents = existingEntry.dependents.filter((d) => d.uuid !== dependentUuid);
      if (existingEntry.dependents.length === 0) {
        this.#entriesCache.delete(parentUuid);
      }
    }
  }
  #buildDependentInfo(uuid) {
    const doc = fromUuidSync(uuid, { strict: false });
    const docType = doc?.documentName ?? "Unknown";
    let depOwnerUuid;
    let depOwnerName;
    let depOwnerIcon;
    let depOwnerExists;
    let depGrandparentUuid;
    let depGrandparentName;
    let depGrandparentIcon;
    let depGrandparentExists;
    if (doc && (docType === "Item" || docType === "ActiveEffect")) {
      const owner = doc.parent;
      if (owner) {
        depOwnerUuid = owner.uuid;
        depOwnerName = owner.name;
        depOwnerIcon = this.#getDocumentIcon(owner);
        depOwnerExists = true;
        if (owner.documentName === "Item" && owner.parent) {
          const grandparent = owner.parent;
          depGrandparentUuid = grandparent.uuid;
          depGrandparentName = grandparent.name;
          depGrandparentIcon = this.#getDocumentIcon(grandparent);
          depGrandparentExists = true;
        }
      }
    }
    return {
      uuid,
      name: doc?.name ?? doc?.documentName ?? i18n("midi-qol.DependentsExplorer.Unknown"),
      type: docType,
      displayType: this.#getDisplayType(docType),
      icon: this.#getDocumentIcon(doc),
      exists: !!doc,
      hasItemParent: doc?.parent instanceof Item,
      ownerUuid: depOwnerUuid,
      ownerName: depOwnerName,
      ownerIcon: depOwnerIcon,
      ownerExists: depOwnerExists,
      grandparentUuid: depGrandparentUuid,
      grandparentName: depGrandparentName,
      grandparentIcon: depGrandparentIcon,
      grandparentExists: depGrandparentExists
    };
  }
  #rebuildCache() {
    this.#entriesCache.clear();
    const rawEntries = MidiDependentsRegistry.getAll();
    for (const { parentUuid, dependentUuids } of rawEntries) {
      const entry = this.#buildParentEntry(parentUuid, dependentUuids);
      if (entry) {
        this.#entriesCache.set(parentUuid, entry);
      }
    }
    this.#cacheValid = true;
  }
  #buildParentEntry(parentUuid, dependentUuids) {
    const parentDoc = fromUuidSync(parentUuid, { strict: false });
    const parentExists = !!parentDoc;
    const parentName = parentDoc?.name ?? parentDoc?.documentName ?? i18n("midi-qol.DependentsExplorer.Unknown");
    const parentType = parentDoc?.documentName ?? "Unknown";
    const dependents = dependentUuids.map((uuid) => this.#buildDependentInfo(uuid)).filter((d) => d !== null);
    let ownerUuid;
    let ownerName;
    let ownerIcon;
    let ownerExists;
    let grandparentUuid;
    let grandparentName;
    let grandparentIcon;
    let grandparentExists;
    if (parentDoc && (parentType === "Item" || parentType === "ActiveEffect")) {
      const owner = parentDoc.parent;
      if (owner) {
        ownerUuid = owner.uuid;
        ownerName = owner.name;
        ownerIcon = this.#getDocumentIcon(owner);
        ownerExists = true;
        if (owner.documentName === "Item" && owner.parent) {
          const grandparent = owner.parent;
          grandparentUuid = grandparent.uuid;
          grandparentName = grandparent.name;
          grandparentIcon = this.#getDocumentIcon(grandparent);
          grandparentExists = true;
        }
      }
    }
    return {
      parentUuid,
      parentName,
      parentType,
      parentIcon: this.#getDocumentIcon(parentDoc),
      parentExists,
      hasItemParent: parentDoc?.parent instanceof Item,
      ownerUuid,
      ownerName,
      ownerIcon,
      ownerExists,
      grandparentUuid,
      grandparentName,
      grandparentIcon,
      grandparentExists,
      dependents
    };
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    const searchInput = this.element.querySelector('input[name="search-filter"]');
    if (searchInput) {
      searchInput.addEventListener("input", this.#onSearchInput.bind(this));
    }
  }
  async _onClose(options) {
    await super._onClose(options);
    for (const [hookName, hookId] of this.#hookIds.entries()) {
      if (hookName === "registryChanged") {
        Hooks.off("midi-qol.dependentsRegistryChanged", hookId);
      } else {
        Hooks.off(hookName, hookId);
      }
    }
    this.#hookIds.clear();
  }
  #onSearchInput(event) {
    const input = event.target;
    this.#searchFilter = input.value;
    this.#debouncedSearchRender();
  }
  #renderSearchResults() {
    const searchInput = this.element.querySelector('input[name="search-filter"]');
    const cursorPosition = searchInput?.selectionStart ?? this.#searchFilter.length;
    this.render({ force: true }).then(() => {
      const newSearchInput = this.element.querySelector('input[name="search-filter"]');
      if (newSearchInput) {
        newSearchInput.setSelectionRange(cursorPosition, cursorPosition);
      }
    });
  }
  static #onClearFilter(event, target) {
    this.#searchFilter = "";
    this.render({ force: true }).then(() => {
      const searchInput = this.element.querySelector('input[name="search-filter"]');
      searchInput?.focus();
    });
  }
  static #onToggleSection(event, target) {
    const type = target.dataset.type;
    if (!type) return;
    if (this.#collapsedSections.has(type)) {
      this.#collapsedSections.delete(type);
    } else {
      this.#collapsedSections.add(type);
    }
    this.render({ force: true });
  }
  static async #onDeleteParent(event, target) {
    const uuid = target.dataset.uuid;
    if (!uuid) return;
    const doc = fromUuidSync(uuid, { strict: false });
    if (!doc) {
      ui.notifications?.warn(i18n("midi-qol.DependentsExplorer.DocumentNotFound"));
      return;
    }
    const docType = doc.documentName;
    const isItemEffect = docType === "ActiveEffect" && doc.parent instanceof Item;
    const needsConfirm = docType === "Actor" || docType === "Item" || isItemEffect;
    if (needsConfirm) {
      const confirmMessage = isItemEffect ? i18n("midi-qol.DependentsExplorer.ConfirmDeleteItemEffect").replace("{name}", doc.name) : i18n("midi-qol.DependentsExplorer.ConfirmDeleteParent").replace("{name}", doc.name).replace("{type}", docType);
      const confirmed = await DialogV23.confirm({
        window: { title: i18n("midi-qol.DependentsExplorer.ConfirmDelete") },
        content: confirmMessage,
        yes: { label: i18n("midi-qol.DependentsExplorer.Delete") },
        no: { label: i18n("midi-qol.DependentsExplorer.Cancel") }
      });
      if (!confirmed) return;
    }
    const docName = doc.name;
    try {
      await doc.delete();
      ui.notifications?.info(i18n("midi-qol.DependentsExplorer.Deleted").replace("{name}", docName));
      this.render({ force: true });
    } catch (err) {
      console.error("DependentsExplorer | Failed to delete document:", err);
      ui.notifications?.error(i18n("midi-qol.DependentsExplorer.DeleteFailed"));
    }
  }
  static async #onDeleteDependent(event, target) {
    const uuid = target.dataset.uuid;
    if (!uuid) return;
    const doc = fromUuidSync(uuid, { strict: false });
    if (!doc) {
      ui.notifications?.warn(i18n("midi-qol.DependentsExplorer.DocumentNotFound"));
      return;
    }
    const docType = doc.documentName;
    const isItemEffect = docType === "ActiveEffect" && doc.parent instanceof Item;
    const needsConfirm = docType === "Actor" || docType === "Item" || isItemEffect;
    if (needsConfirm) {
      const confirmMessage = isItemEffect ? i18n("midi-qol.DependentsExplorer.ConfirmDeleteItemEffect").replace("{name}", doc.name) : i18n("midi-qol.DependentsExplorer.ConfirmDeleteDependent").replace("{name}", doc.name).replace("{type}", docType);
      const confirmed = await DialogV23.confirm({
        window: { title: i18n("midi-qol.DependentsExplorer.ConfirmDelete") },
        content: confirmMessage,
        yes: { label: i18n("midi-qol.DependentsExplorer.Delete") },
        no: { label: i18n("midi-qol.DependentsExplorer.Cancel") }
      });
      if (!confirmed) return;
    }
    const docName = doc.name;
    try {
      await doc.delete();
      ui.notifications?.info(i18n("midi-qol.DependentsExplorer.Deleted").replace("{name}", docName));
      this.render({ force: true });
    } catch (err) {
      console.error("DependentsExplorer | Failed to delete document:", err);
      ui.notifications?.error(i18n("midi-qol.DependentsExplorer.DeleteFailed"));
    }
  }
  static async #onOpenDocument(event, target) {
    const uuid = target.dataset.uuid;
    if (!uuid) return;
    const doc = fromUuidSync(uuid, { strict: false });
    if (!doc) {
      ui.notifications?.warn(i18n("midi-qol.DependentsExplorer.DocumentNotFound"));
      return;
    }
    if (typeof doc.sheet?.render === "function") {
      doc.sheet.render(true);
    }
  }
};

// src/module/apps/RollStatsDisplay.ts
var { ApplicationV2: ApplicationV25, HandlebarsApplicationMixin: HandlebarsApplicationMixin5 } = foundry.applications.api;
var RollStatsDisplay = class _RollStatsDisplay extends HandlebarsApplicationMixin5(ApplicationV25) {
  static {
    __name(this, "RollStatsDisplay");
  }
  statsHookId;
  playersOnly;
  stats;
  expanded;
  expandedItems;
  showLifetime;
  #renderPending = false;
  constructor(options = {}) {
    super(options);
    this.stats = options.stats ?? gameStats;
    this.playersOnly = options.playersOnly ?? configSettings.playerStatsOnly ?? !game.user?.isGM;
    this.expanded = {};
    this.expandedItems = {};
    this.showLifetime = {};
    Object.keys(this.stats.currentStats).forEach((aid) => {
      this.expanded[aid] = this.playersOnly;
    });
    this.statsHookId = Hooks.on("midi-qol.StatsUpdated", () => {
      if (this.#renderPending) return;
      this.#renderPending = true;
      setTimeout(() => {
        this.#renderPending = false;
        this.render({ force: true });
      }, 500);
    });
  }
  static DEFAULT_OPTIONS = {
    id: "midi-qol-statistics",
    window: {
      title: "midi-qol.StatsTitle",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      width: 820,
      height: 650
    },
    actions: {
      clearStats: _RollStatsDisplay.#onClearStats,
      exportJSON: _RollStatsDisplay.#onExportJSON,
      exportCSV: _RollStatsDisplay.#onExportCSV,
      endSession: _RollStatsDisplay.#onEndSession,
      removeStats: _RollStatsDisplay.#onRemoveStats,
      toggleExpand: _RollStatsDisplay.#onToggleExpand,
      toggleItems: _RollStatsDisplay.#onToggleItems,
      togglePeriod: _RollStatsDisplay.#onTogglePeriod
    }
  };
  static PARTS = {
    tabs: { template: "templates/generic/tab-navigation.hbs" },
    stats: { template: "modules/midi-qol/templates/apps/RollStatsDisplay/stats.hbs", scrollable: ["", ".midi-stats-card-body"] },
    config: { template: "modules/midi-qol/templates/apps/RollStatsDisplay/config.hbs", scrollable: [""] }
  };
  tabGroups = {
    sheet: "stats"
  };
  get title() {
    return i18n("midi-qol.StatsTitle");
  }
  _onClose(options) {
    super._onClose(options);
    Hooks.off("midi-qol.StatsUpdated", this.statsHookId);
    this.stats.saveStats();
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.stats = this.stats.prepareStats();
    Object.keys(context.stats).forEach((id) => {
      if (this.playersOnly && game.user?.id !== id && game.actors?.get(id)?.permission !== CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
        delete context.stats[id];
      }
    });
    Object.keys(context.stats).forEach((id) => {
      const s = context.stats[id];
      s.hasItemStats = Object.keys(s.itemStats).length > 0;
      const sDealt = s.sessionDamageDealtByType ?? {};
      const sApplied = s.sessionDamageAppliedByType ?? {};
      const lDealt = s.lifetimeDamageDealtByType ?? {};
      const lApplied = s.lifetimeDamageAppliedByType ?? {};
      const dealtTypes = [.../* @__PURE__ */ new Set([
        ...Object.keys(sDealt),
        ...Object.keys(lDealt),
        ...Object.keys(sApplied),
        ...Object.keys(lApplied)
      ])];
      s.damageDealtTypes = dealtTypes;
      s.sessionDamageByType = dealtTypes.map((type) => ({
        dealt: sDealt[type] ?? 0,
        applied: sApplied[type] ?? 0
      }));
      s.lifetimeDamageByType = dealtTypes.map((type) => ({
        dealt: lDealt[type] ?? 0,
        applied: lApplied[type] ?? 0
      }));
      const sTaken = s.sessionDamageTakenByType ?? {};
      const lTaken = s.lifetimeDamageTakenByType ?? {};
      const takenTypes = [.../* @__PURE__ */ new Set([...Object.keys(sTaken), ...Object.keys(lTaken)])];
      s.damageTakenTypes = takenTypes;
      s.sessionDmgTaken = takenTypes.map((type) => sTaken[type] ?? 0);
      s.lifetimeDmgTaken = takenTypes.map((type) => lTaken[type] ?? 0);
    });
    context.hasStats = Object.keys(context.stats).length > 0;
    context.isGm = game.user?.isGM;
    context.configSettings = configSettings;
    context.tabs = this.#getTabs();
    return context;
  }
  _preparePartContext(partId, context) {
    if (Object.keys(context.tabs).includes(partId)) {
      context.tab = context.tabs[partId];
    }
    return context;
  }
  #getTabs() {
    const tabs = {
      stats: { id: "stats", group: "sheet", label: i18n("midi-qol.RollStats"), active: true },
      config: { id: "config", group: "sheet", label: i18n("midi-qol.RollStatsConfig") }
    };
    return tabs;
  }
  async _onRender(context, options) {
    super._onRender(context, options);
    for (const name of ["keepRollStats", "playerStatsOnly"]) {
      this.element.querySelector(`input[name="${name}"]`)?.addEventListener("change", (e) => {
        configSettings[name] = e.target.checked;
        game.settings.set("midi-qol", "ConfigSettings", configSettings);
      });
    }
    this.element.querySelector('input[name="saveStatsEvery"]')?.addEventListener("change", (e) => {
      configSettings.saveStatsEvery = Number(e.target.value);
      game.settings.set("midi-qol", "ConfigSettings", configSettings);
    });
    Object.keys(this.stats.currentStats).forEach((id) => {
      const card = this.element.querySelector(`.midi-stats-card[data-actor-id="${id}"]`);
      if (card) {
        if (this.expanded[id]) card.classList.add("expanded");
        if (this.showLifetime[id]) card.classList.add("show-lifetime");
      }
      const itemsSection = this.element.querySelector(`.midi-stats-items-section[data-actor-id="${id}"]`);
      if (itemsSection && this.expandedItems[id]) {
        itemsSection.classList.add("expanded");
      }
    });
  }
  static #onClearStats() {
    this.stats.clearStats();
  }
  static #onExportJSON() {
    this.stats.exportToJSON();
  }
  static #onExportCSV() {
    this.stats.exportToCSV();
  }
  static #onEndSession() {
    this.stats.endSession();
  }
  static #onRemoveStats(event, target) {
    const actorId = target.dataset.actorId;
    if (actorId) {
      this.stats.clearActorStats(actorId);
      this.render({ force: true });
    }
  }
  static #onToggleExpand(event, target) {
    const actorId = target.dataset.actorId;
    if (!actorId) return;
    this.expanded[actorId] = !this.expanded[actorId];
    const card = target.closest(".midi-stats-card");
    if (card) {
      card.classList.toggle("expanded");
    }
  }
  static #onToggleItems(event, target) {
    const actorId = target.dataset.actorId;
    if (!actorId) return;
    this.expandedItems[actorId] = !this.expandedItems[actorId];
    const section = target.closest(".midi-stats-items-section");
    if (section) {
      section.classList.toggle("expanded");
    }
  }
  static #onTogglePeriod(event, target) {
    event.stopPropagation();
    const actorId = target.dataset.actorId;
    if (!actorId) return;
    this.showLifetime[actorId] = !this.showLifetime[actorId];
    const card = target.closest(".midi-stats-card");
    if (card) {
      card.classList.toggle("show-lifetime", this.showLifetime[actorId]);
    }
  }
};

// src/module/midi-sounds.ts
var MidiSounds = class _MidiSounds {
  static {
    __name(this, "MidiSounds");
  }
  static weaponBaseTypes;
  static getSound(playListName, soundName) {
    const playlist = game.playlists?.getName(playListName);
    return { playlist, sound: playlist?.sounds.getName(soundName) };
  }
  static async playSound(playListName, soundName) {
    const { playlist, sound } = this.getSound(playListName, soundName);
    if (playlist && sound?.path) {
      return foundry.audio.AudioHelper.play({ src: sound.path, volume: sound.volume, loop: false }, true);
    }
    return null;
  }
  static ActionTypes() {
    const damageEntries = Object.fromEntries(Object.entries(GameSystemConfig.damageTypes).map(([key, { label }]) => [key, `${i18n("DND5E.Damage")}: ${label}`]));
    const itemActionEntries = Object.fromEntries(Object.entries(GameSystemConfig.itemActionTypes).map(([key, label]) => [key, `${i18n("DND5E.Action")}: ${label}`]));
    return {
      itemRoll: `${i18n("DOCUMENT.Item")} ${i18n("TABLE.Roll")}`,
      attack: i18n("DND5E.AttackRoll"),
      damage: i18n("DND5E.DamageRoll"),
      critical: `${i18n("DND5E.Attack")}: ${i18n("midi-qol.CriticalSoundName")}`,
      fumble: `${i18n("DND5E.Attack")}: ${i18n("midi-qol.FumbleSoundName")}`,
      hit: `${i18n("DND5E.Attack")}: ${i18n("midi-qol.Hits")}`,
      miss: `${i18n("DND5E.Attack")}: ${i18n("midi-qol.Misses")}`,
      none: i18n("None"),
      ...itemActionEntries,
      ...damageEntries
    };
  }
  static async playRandomSound(playListName) {
    const playlist = game.playlists?.getName(playListName);
    if (playlist) {
      const sounds = playlist.sounds;
      const soundIndex = Math.floor(Math.random() * sounds.contents.length);
      const sound = sounds.contents[soundIndex];
      return this.playSound(playlist.name, sound.name);
    }
    return null;
  }
  static getSubtype(item) {
    if (!item?.type) return "";
    let subtype = "";
    switch (item.type) {
      // @ts-expect-error no dnd5e-types
      case "weapon":
        subtype = "weapon." + item.system.type.value;
        break;
      // @ts-expect-error no dnd5e-types
      case "equipment":
        subtype = "equipment." + item.system.type.value;
        break;
      // @ts-expect-error no dnd5e-types
      case "consumable":
        subtype = "consumable." + item.system.type.value;
        break;
      // @ts-expect-error no dnd5e-types
      case "spell":
        subtype = "spell." + item.system.school;
        break;
      // @ts-expect-error no dnd5e-types
      case "tool":
        subtype = "tool." + item.system.type.value;
        break;
      default:
        subtype = item.type + "any";
    }
    return subtype;
  }
  static async getWeaponBaseTypes() {
    _MidiSounds.weaponBaseTypes = {};
    const packname = GameSystemConfig.sourcePacks.ITEMS;
    if (packname) {
      const packObject = game.packs?.get(packname);
      await packObject?.getIndex({ fields: ["system.armor.type", "system.toolType", "system.weaponType", "img"] });
      const weaponTypes = Object.keys(GameSystemConfig.weaponTypes);
      for (const wt of weaponTypes) {
        const baseTypes = await _MidiSounds.getItemBaseTypes("weapon", wt);
        foundry.utils.mergeObject(_MidiSounds.weaponBaseTypes, baseTypes);
      }
    }
    debug("Weapon base types are ", _MidiSounds.weaponBaseTypes);
  }
  static async getItemBaseTypes(type, weaponType) {
    const baseIds = GameSystemConfig[`${type}Ids`];
    if (baseIds === void 0) return {};
    const typeProperty = `type.value`;
    const baseType = weaponType;
    const items = {};
    for (const [name, id] of Object.entries(baseIds)) {
      let baseItem = await globalThis.dnd5e.documents.Trait.getBaseItem(id);
      if (baseType !== baseItem && foundry.utils.getProperty(baseItem.system, typeProperty)) continue;
      items[name] = baseItem?.name;
    }
    const result = Object.fromEntries(Object.entries(items).sort((lhs, rhs) => lhs[1].localeCompare(rhs[1])));
    return result;
  }
  static getSpecFor(actorType, type, subtype, weaponSubType, selector) {
    let spec;
    for (let aType of [actorType, "any"]) {
      let specs = foundry.utils.getProperty(midiSoundSettings, aType) ?? {};
      if (!spec) spec = foundry.utils.getProperty(specs, `weapon.${weaponSubType}.${selector}`);
      if (!spec) spec = foundry.utils.getProperty(specs, `${subtype}.${selector}`);
      if (!spec) spec = foundry.utils.getProperty(specs, `${type}.any.${selector}`);
      if (!spec) spec = foundry.utils.getProperty(specs, `all.any.${selector}`);
      if (spec) return spec;
    }
    return void 0;
  }
  static playSpec(spec) {
    if (spec.soundName !== "random")
      return this.playSound(spec.playlistName, spec.soundName);
    else
      return this.playRandomSound(spec.playlistName);
  }
  static processHook(workflow, selector) {
    const subtype = this.getSubtype(workflow.item);
    const baseType = workflow.item.system.type?.baseItem ?? "";
    let spec = this.getSpecFor(workflow.item.parent?.type ?? "all", workflow.item.type ?? "any", subtype, baseType, selector);
    if (!spec) return false;
    return this.playSpec(spec);
  }
  static midiSoundsReadyHooks() {
    Hooks.on("midi-qol.preItemRoll", async (workflow) => {
      if (!configSettings.useCustomSounds || !workflow.item) return true;
      await this.processHook(workflow, "itemRoll");
      return true;
    });
    Hooks.on("midi-qol.preAttackRoll", async (workflow) => {
      if (!configSettings.useCustomSounds || !workflow.item) return true;
      if (dice3dEnabled() && workflow.activity.hasAttack && !await this.processHook(workflow, workflow.activity.actionType)) {
        await this.processHook(workflow, "attack");
      }
      return true;
    });
    Hooks.on("midi-qol.AttackRollComplete", async (workflow) => {
      if (!configSettings.useCustomSounds || !workflow.item) return true;
      if (!dice3dEnabled() && workflow.activity.hasAttack && !await this.processHook(workflow, workflow.activity.actionType)) {
        await this.processHook(workflow, "attack");
      }
      if (workflow.isCritical) {
        await this.processHook(workflow, "critical");
      } else if (workflow.isFumble) {
        await this.processHook(workflow, "fumble");
      } else if (workflow.hitTargets.size === 0) {
        await this.processHook(workflow, "miss");
      } else {
        await this.processHook(workflow, "hit");
      }
      return true;
    });
    Hooks.on("midi-qol.DamageRollComplete", async (workflow) => {
      if (!configSettings.useCustomSounds || !workflow.item) return true;
      const result = await this.processHook(workflow, workflow.defaultDamageType);
      if (!result)
        await this.processHook(workflow, "damage");
      return true;
    });
  }
  static async createDefaultPlayList() {
    if (!game.user?.isGM) {
      ui.notifications?.warn("Only a GM can create the default playlist");
      return;
    }
    if (game.playlists?.getName("Midi Item Tracks") !== void 0) {
      ui.notifications?.error("Midi Item Tracks already exists - delete it before creating the default playlist");
      return;
    }
    const playlistData = {
      name: "Midi Item Tracks",
      description: "Midi Qol sample custom sounds",
      mode: CONST.PLAYLIST_MODES.DISABLED,
      sounds: [
        {
          path: "modules/midi-qol/sounds/success-drums.ogg",
          repeat: false,
          volume: 0.125,
          name: "success-drums",
          playing: false,
          pausedTime: null,
          sort: 0
        },
        {
          name: "dice",
          description: "",
          path: "sounds/dice.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "drink",
          description: "",
          path: "modules/midi-qol/sounds/drink.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "fail1",
          description: "",
          path: "modules/midi-qol/sounds/fail1.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "fail2",
          description: "",
          path: "modules/midi-qol/sounds/fail2.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "good-results",
          description: "",
          path: "modules/midi-qol/sounds/good-results.ogg",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "spell",
          description: "",
          path: "modules/midi-qol/sounds/spell.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "success",
          description: "",
          path: "modules/midi-qol/sounds/success.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "swing",
          description: "",
          path: "modules/midi-qol/sounds/swing.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "use",
          description: "",
          path: "modules/midi-qol/sounds/use.wav",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "fail3",
          description: "",
          path: "modules/midi-qol/sounds/fail3.ogg",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        },
        {
          name: "bowshot",
          description: "",
          path: "modules/midi-qol/sounds/bow-and-arrow.mp3",
          playing: false,
          pausedTime: null,
          repeat: false,
          volume: 0.5240467536394058,
          sort: 0
        }
      ]
    };
    return Playlist.create(playlistData, {});
  }
  static async setupDetailedSounds() {
    const soundSettings = {
      version: "0.9.48",
      any: {
        all: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "dice" },
            attack: { playlistName: "Midi Item Tracks", soundName: "dice" },
            damage: { playlistName: "Midi Item Tracks", soundName: "dice" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        consumable: {
          ammo: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "none" }
          },
          food: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          poison: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          potion: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          rod: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          },
          scroll: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          },
          trinket: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" }
          },
          wand: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        weapon: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "none" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        spell: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "none" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        feat: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" }
          }
        },
        tool: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" }
          }
        }
      }
    };
    if (game.user?.can("SETTINGS_MODIFY")) await game.settings.set("midi-qol", "MidiSoundSettings", soundSettings);
  }
  static async setupFullSounds() {
    const soundSettings = {
      version: "0.9.48",
      any: {
        all: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "dice" },
            attack: { playlistName: "Midi Item Tracks", soundName: "dice" },
            damage: { playlistName: "Midi Item Tracks", soundName: "dice" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" },
            hit: { playlistName: "Midi Item Tracks", soundName: "none" },
            miss: { playlistName: "Midi Item Tracks", soundName: "none" },
            abil: { playlistName: "Midi Item Tracks", soundName: "dice" },
            heal: { playlistName: "Midi Item Tracks", soundName: "dice" },
            msak: { playlistName: "Midi Item Tracks", soundName: "dice" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "dice" },
            other: { playlistName: "Midi Item Tracks", soundName: "dice" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "dice" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "dice" },
            save: { playlistName: "Midi Item Tracks", soundName: "dice" },
            util: { playlistName: "Midi Item Tracks", soundName: "dice" },
            acid: { playlistName: "Midi Item Tracks", soundName: "dice" },
            bludgeoning: { playlistName: "Midi Item Tracks", soundName: "dice" },
            cold: { playlistName: "Midi Item Tracks", soundName: "dice" },
            fire: { playlistName: "Midi Item Tracks", soundName: "dice" },
            force: { playlistName: "Midi Item Tracks", soundName: "dice" },
            lightning: { playlistName: "Midi Item Tracks", soundName: "dice" },
            "midi-none": { playlistName: "Midi Item Tracks", soundName: "dice" },
            necrotic: { playlistName: "Midi Item Tracks", soundName: "dice" },
            piercing: { playlistName: "Midi Item Tracks", soundName: "dice" },
            poison: { playlistName: "Midi Item Tracks", soundName: "dice" },
            psychic: { playlistName: "Midi Item Tracks", soundName: "dice" },
            radiant: { playlistName: "Midi Item Tracks", soundName: "dice" },
            slashing: { playlistName: "Midi Item Tracks", soundName: "dice" },
            thunder: { playlistName: "Midi Item Tracks", soundName: "dice" }
          }
        },
        consumable: {
          ammo: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "none" }
          },
          food: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          poison: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          potion: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          rod: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          },
          scroll: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          },
          trinket: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" }
          },
          wand: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        weapon: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "none" },
            abil: { playlistName: "Midi Item Tracks", soundName: "swing" },
            heal: { playlistName: "Midi Item Tracks", soundName: "spell" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            other: { playlistName: "Midi Item Tracks", soundName: "swing" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            save: { playlistName: "Midi Item Tracks", soundName: "swing" },
            util: { playlistName: "Midi Item Tracks", soundName: "swing" },
            damage: { playlistName: "Midi Item Tracks", soundName: "dice" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" },
            hit: { playlistName: "Midi Item Tracks", soundName: "none" },
            miss: { playlistName: "Midi Item Tracks", soundName: "none" }
          }
        },
        spell: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" },
            abil: { playlistName: "Midi Item Tracks", soundName: "spell" },
            heal: { playlistName: "Midi Item Tracks", soundName: "spell" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            other: { playlistName: "Midi Item Tracks", soundName: "spell" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            save: { playlistName: "Midi Item Tracks", soundName: "spell" },
            util: { playlistName: "Midi Item Tracks", soundName: "spell" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" },
            hit: { playlistName: "Midi Item Tracks", soundName: "none" },
            miss: { playlistName: "Midi Item Tracks", soundName: "none" }
          }
        },
        feat: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" },
            abil: { playlistName: "Midi Item Tracks", soundName: "none" },
            heal: { playlistName: "Midi Item Tracks", soundName: "none" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            other: { playlistName: "Midi Item Tracks", soundName: "none" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            save: { playlistName: "Midi Item Tracks", soundName: "spell" },
            util: { playlistName: "Midi Item Tracks", soundName: "none" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" },
            hit: { playlistName: "Midi Item Tracks", soundName: "none" },
            miss: { playlistName: "Midi Item Tracks", soundName: "none" }
          }
        },
        tool: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" }
          }
        }
      }
    };
    if (game.user?.can("SETTINGS_MODIFY")) await game.settings.set("midi-qol", "MidiSoundSettings", soundSettings);
  }
  static async setupBasicSounds() {
    const soundSettings = {
      version: "0.9.48",
      any: {
        all: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "dice" },
            attack: { playlistName: "Midi Item Tracks", soundName: "dice" },
            damage: { playlistName: "Midi Item Tracks", soundName: "dice" },
            critical: { playlistName: "Midi Item Tracks", soundName: "success-drums" },
            fumble: { playlistName: "Midi Item Tracks", soundName: "fail1" },
            mwak: { playlistName: "Midi Item Tracks", soundName: "swing" },
            rwak: { playlistName: "Midi Item Tracks", soundName: "bowshot" },
            rsak: { playlistName: "Midi Item Tracks", soundName: "spell" },
            msak: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        consumable: {
          ammo: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "none" }
          },
          food: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          poison: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          potion: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "drink" }
          },
          rod: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          },
          scroll: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          },
          trinket: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" }
          },
          wand: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "spell" }
          }
        },
        tool: {
          any: {
            itemRoll: { playlistName: "Midi Item Tracks", soundName: "use" }
          }
        }
      }
    };
    if (game.user?.can("SETTINGS_MODIFY")) await game.settings.set("midi-qol", "MidiSoundSettings", soundSettings);
  }
};

// src/module/apps/SoundConfigPanel.ts
var { ApplicationV2: ApplicationV26, HandlebarsApplicationMixin: HandlebarsApplicationMixin6 } = foundry.applications.api;
var SoundConfigPanel = class _SoundConfigPanel extends HandlebarsApplicationMixin6(ApplicationV26) {
  static {
    __name(this, "SoundConfigPanel");
  }
  static defaultPlaylist;
  static defaultSound;
  get title() {
    return i18n("midi-qol.ConfigTitle") ?? "Sound Config";
  }
  static DEFAULT_OPTIONS = {
    id: "midi-qol-sound-config",
    window: {
      title: "midi-qol.SoundSettings.Name",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      height: "auto",
      width: 900
    },
    actions: {
      add: this.#onAdd,
      delete: this.#onDelete,
      save: this.#onSave
    },
    form: {
      submitOnChange: false,
      closeOnSubmit: false
    },
    tag: "form"
  };
  static PARTS = {
    tabs: { template: "templates/generic/tab-navigation.hbs" },
    sounds: {
      template: "modules/midi-qol/templates/apps/SoundConfigPanel/sounds.hbs",
      scrollable: [""]
    },
    quick: { template: "modules/midi-qol/templates/apps/SoundConfigPanel/quick.hbs" },
    footer: { template: "templates/generic/form-footer.hbs" }
  };
  tabGroups = {
    sheet: "sounds"
  };
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    const consumableSubtypesData = Object.keys(GameSystemConfig.consumableTypes).reduce((obj, key) => {
      obj[key] = GameSystemConfig.consumableTypes[key].label;
      return obj;
    }, {});
    const spellSchoolData = Object.keys(GameSystemConfig.spellSchools).reduce((obj, key) => {
      obj[key] = GameSystemConfig.spellSchools[key].label;
      return obj;
    }, {});
    const itemTypes = foundry.utils.duplicate(CONFIG.Item.typeLabels);
    delete itemTypes.backpack;
    delete itemTypes.class;
    itemTypes.all = "All";
    itemTypes.none = "None";
    context.weaponSubtypes = foundry.utils.mergeObject({ any: "Any", none: "None" }, GameSystemConfig.weaponTypes);
    context.weaponSubtypes = foundry.utils.mergeObject(context.weaponSubtypes, MidiSounds.weaponBaseTypes);
    context.equipmentSubtypes = foundry.utils.mergeObject({ any: "Any" }, GameSystemConfig.equipmentTypes);
    context.consumableSubTypes = foundry.utils.mergeObject({ any: "Any" }, consumableSubtypesData);
    context.spellSubtypes = foundry.utils.mergeObject({ any: "Any" }, spellSchoolData);
    context.toolSubtypes = foundry.utils.mergeObject({ any: "Any" }, GameSystemConfig.toolTypes);
    context.defaultSubtypes = { any: "Any" };
    context.characterTypes = { any: "Any", npc: "NPC", character: "Character", "none": "None" };
    context.subTypes = {
      "weapon": context.weaponSubtypes,
      "equipment": context.equipmentSubtypes,
      "consumable": context.consumableSubTypes,
      "spell": context.spellSubtypes,
      "tool": context.toolSubtypes,
      "feat": { any: "Any" },
      "all": context.defaultSubtypes
    };
    context.itemTypes = Object.keys(itemTypes).reduce((list, key) => {
      if (!context.subTypes[key]) context.subTypes[key] = { "any": "Any" };
      list[key] = i18n(itemTypes[key]);
      return list;
    }, {});
    context.midiSoundSettings = foundry.utils.duplicate(midiSoundSettings);
    context.quickSettingsOptions = { createPlaylist: "Create Sample Playlist", basic: "Basic Settings", detailed: "Detailed Settings", full: "Full Settings" };
    context.playlists = this.playlists;
    const requiredPlayLists = this.requiredPlaylists;
    context.missingPlaylists = [];
    for (let playlistName of Object.keys(requiredPlayLists)) {
      if (!context.playlists[playlistName]) {
        context.playlists[playlistName] = { "none": "none" };
        context.missingPlaylists.push(playlistName);
      }
      for (let soundName of Array.from(requiredPlayLists[playlistName])) {
        if (!context.playlists[playlistName]?.[soundName]) {
          if (!context.playlists[playlistName]) context.playlists[playlistName] = {};
          context.playlists[playlistName][soundName] = `Missing:${soundName}`;
        }
      }
    }
    context.actionTypes = MidiSounds.ActionTypes();
    context.tabs = this.#getTabs();
    _SoundConfigPanel.defaultPlaylist = Object.keys(context.playlists ?? {})[0];
    _SoundConfigPanel.defaultSound = Object.keys(context.playlists[_SoundConfigPanel.defaultPlaylist] ?? {})[0] ?? "";
    context.buttons = [{
      icon: "fas fa-floppy-disk",
      action: "save",
      label: "Save Changes",
      type: "button"
    }];
    return context;
  }
  get playlists() {
    return game.playlists?.reduce((acc, pl) => {
      acc[pl.name] = pl.sounds.reduce((list, sound) => {
        list[sound.name] = sound.name;
        return list;
      }, {});
      acc[pl.name].none = "none";
      acc[pl.name].random = "random";
      return acc;
    }, {}) ?? {};
  }
  get requiredPlaylists() {
    const requiredPlayLists = {};
    for (let [cek, ce] of Object.entries(midiSoundSettings)) {
      if (cek === "version") continue;
      for (let [tek, te] of Object.entries(ce)) {
        for (let [stek, ste] of Object.entries(te)) {
          for (let [eek, ee] of Object.entries(ste)) {
            if (!requiredPlayLists[ee.playlistName]) {
              requiredPlayLists[ee.playlistName] = /* @__PURE__ */ new Set();
            }
            requiredPlayLists[ee.playlistName].add(ee.soundName);
          }
        }
      }
    }
    return requiredPlayLists;
  }
  missingSounds(soundSettings) {
    const missingSounds = [];
    let missingPlaylists = [];
    const requiredPlayLists = this.requiredPlaylists;
    const playlists = this.playlists;
    for (let playlistName of Object.keys(requiredPlayLists)) {
      if (!playlists[playlistName]) {
        playlists[playlistName] = {};
        missingPlaylists.push(playlistName);
      }
      for (let soundName of Array.from(requiredPlayLists[playlistName])) {
        if (!playlists[playlistName][soundName]) {
          missingSounds.push(`${playlistName}: ${soundName}`);
        }
      }
    }
    return missingSounds;
  }
  canSave(soundSettings) {
    let missingPlaylists = [];
    const requiredPlayLists = this.requiredPlaylists;
    const playlists = this.playlists;
    let canSave = true;
    const missingSounds = [];
    for (let playlistName of Object.keys(requiredPlayLists)) {
      if (!playlists[playlistName]) {
        playlists[playlistName] = {};
        missingPlaylists.push(playlistName);
        canSave = false;
      }
      for (let soundName of Array.from(requiredPlayLists[playlistName])) {
        if (!playlists[playlistName][soundName]) {
          playlists[playlistName][soundName] = `${soundName} missing`;
          missingSounds.push(`${playlistName}:${soundName}`);
        }
      }
    }
    return canSave;
  }
  #getTabs() {
    const tabs = {
      sounds: { id: "sounds", group: "sheet", label: "midi-qol.SoundSettings.Name", active: true },
      quick: { id: "quick", group: "sheet", label: "midi-qol.Config.Tabs.quick" }
    };
    return tabs;
  }
  async _preparePartContext(partId, context) {
    if (Object.keys(context.tabs).includes(partId)) {
      context.tab = context.tabs[partId];
    }
    return context;
  }
  static async #onAdd(event, target) {
    if (!_SoundConfigPanel.defaultPlaylist) {
      ui.notifications?.error("Midi Sound Configuration: You must define at least one playlist before adding a sound");
      event.preventDefault();
      return;
    }
    if (!game.user?.can("SETTINGS_MODIFY")) return;
    event.preventDefault();
    const formData = new foundry.applications.ux.FormDataExtended(target.form);
    const fdo = formData.object;
    for (let key of ["chartype", "action", "category", "playlistName", "soundName", "subtype"]) {
      if (!fdo[key]) fdo[key] = [];
      if (typeof fdo[key] === "string") fdo[key] = [fdo[key]];
      if (key !== "playlistName") fdo[key].push("none");
      else fdo[key].push(_SoundConfigPanel.defaultPlaylist ?? "none");
    }
    await _SoundConfigPanel.#onSubmit.bind(this)(event, target, formData);
    this.render({ force: true });
  }
  static #onDelete(event, target) {
    event.preventDefault();
    const rowTarget = target.closest("tr");
    rowTarget.remove();
  }
  static async #onSave(event, target) {
    const form = event.currentTarget;
    const formData = new foundry.applications.ux.FormDataExtended(form);
    _SoundConfigPanel.#onSubmit.bind(this)(event, form, formData);
  }
  static async #onSubmit(event, form, formDataIn) {
    if (!game.user?.can("SETTINGS_MODIFY")) return;
    if (!game.user?.can("SETTINGS_MODIFY")) return;
    const formData = formDataIn.object;
    const settings = {};
    if (formData.chartype) {
      if (typeof formData.chartype === "string") {
        for (let key of ["chartype", "action", "category", "playlistName", "soundName", "subtype"]) {
          formData[key] = [formData[key]];
        }
      }
      for (let i = 0; i < (formData.chartype?.length ?? 0); i++) {
        const chartype = formData.chartype[i];
        const category = formData.category[i];
        const subtype = formData.subtype[i];
        const action = formData.action[i];
        const playlistName = formData.playlistName[i];
        const soundName = formData.soundName[i];
        if (!settings[chartype]) settings[chartype] = {};
        if (!settings[chartype][category]) settings[chartype][category] = {};
        if (!settings[chartype][category][subtype]) settings[chartype][category][subtype] = {};
        settings[chartype][category][subtype][action] = { playlistName, soundName };
      }
    }
    if (this.canSave(settings) === false) {
      ui.notifications?.warn(`Missing ${this.missingSounds(settings).join("<br>")}`);
    }
    settings.version = "12.4.40";
    await game.settings.set("midi-qol", "MidiSoundSettings", settings);
    this.render();
  }
  _onChangeForm(formConfig, event) {
    super._onChangeForm(formConfig, event);
    _SoundConfigPanel.#onSubmit.bind(this)(event, this.element, new foundry.applications.ux.FormDataExtended(this.element)).then(() => this.render({}));
  }
  async _onRender(context, options) {
    super._onRender(context, options);
    for (const elem of Array.from(this.element.querySelectorAll(".import-quick-setting"))) {
      elem.addEventListener("click", function(event) {
        const key = event.currentTarget?.id;
        switch (key) {
          case "createPlaylist":
            MidiSounds.createDefaultPlayList().then(() => this.render(true));
            break;
          case "basic":
            MidiSounds.setupBasicSounds().then(() => this.render(true));
            break;
          case "detailed":
            MidiSounds.setupDetailedSounds().then(() => this.render(true));
            break;
          case "full":
            MidiSounds.setupFullSounds().then(() => this.render(true));
            break;
        }
      }.bind(this));
    }
  }
  changeHandler(event) {
    if (!this.element.querySelector("form")) return;
    this.render({ force: true });
  }
};

// src/module/apps/TargetConfirmationConfig.ts
var { ApplicationV2: ApplicationV27, HandlebarsApplicationMixin: HandlebarsApplicationMixin7 } = foundry.applications.api;
var TargetConfirmationConfig = class _TargetConfirmationConfig extends HandlebarsApplicationMixin7(ApplicationV27) {
  static {
    __name(this, "TargetConfirmationConfig");
  }
  gridMappings = {
    "midi-qol-grid1": { x: -1, y: -1 },
    "midi-qol-grid2": { x: 0, y: -1 },
    "midi-qol-grid3": { x: 1, y: -1 },
    "midi-qol-grid4": { x: -1, y: 0 },
    "midi-qol-grid5": { x: 0, y: 0 },
    "midi-qol-grid6": { x: 1, y: 0 },
    "midi-qol-grid7": { x: -1, y: 1 },
    "midi-qol-grid8": { x: 0, y: 1 },
    "midi-qol-grid9": { x: 1, y: 1 }
  };
  selectedPosition = targetConfirmation.gridPosition ?? foundry.utils.duplicate(defaultTargetConfirmationSettings.gridPosition);
  static PARTS = {
    main: { template: "modules/midi-qol/templates/apps/TargetConfirmationConfig/targetConfirmationConfig.hbs" }
  };
  static DEFAULT_OPTIONS = {
    id: "midi-qol-target-confirmation-config",
    window: {
      title: "midi-qol.ConfigTitle",
      contentClasses: ["standard-form"]
    },
    position: {
      width: 400,
      height: "auto"
    },
    form: {
      closeOnSubmit: true
    },
    actions: {
      submit: _TargetConfirmationConfig.#onSubmit
    }
  };
  // scrollY: [".tab.workflow"],
  // tabs: [{ navSelector: ".tabs", contentSelector: ".content", initial: "gm" }]
  async _updateObject(event, formData) {
    formData = foundry.utils.expandObject(formData);
    let newSettings = foundry.utils.mergeObject(targetConfirmation, formData, { overwrite: true, inplace: false });
    if (!newSettings.enabled) {
      newSettings = foundry.utils.duplicate(defaultTargetConfirmationSettings);
      newSettings.gridPosition = formData.gridPosition;
    }
    game.settings.set("midi-qol", "TargetConfirmation", newSettings);
    this.render({ force: true });
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    const selected = Object.keys(this.gridMappings).find((key) => this.gridMappings[key].x === this.selectedPosition.x && this.gridMappings[key].y === this.selectedPosition.y);
    this.element.querySelector(`#${selected}`)?.classList.add("selected");
    const checkboxes = this.element.querySelectorAll('form .form-group input[type="checkbox"]');
    checkboxes.forEach((checkbox) => {
      checkbox.addEventListener("click", () => {
        targetConfirmation[checkbox.name] = !targetConfirmation[checkbox.name];
        this.render({ force: true });
      });
    });
    const gridItems = this.element.querySelectorAll(".midi-qol .grid-item");
    gridItems.forEach((item) => {
      item.addEventListener("click", () => {
        gridItems.forEach((other) => {
          other.classList.remove("selected");
        });
        item.classList.toggle("selected");
        this.selectedPosition = this.gridMappings[item.id];
      });
    });
  }
  get title() {
    return i18n("Target Confirmation Config") ?? "Target Confirmation Config";
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.targetConfirmation = targetConfirmation;
    return context;
  }
  async _preClose(options) {
    targetConfirmation.gridPosition = this.selectedPosition;
    await game.settings.set("midi-qol", "TargetConfirmation", targetConfirmation);
  }
  static async #onSubmit(event, target) {
    this.close();
  }
};

// src/module/chatMessageHandling.ts
var colorChatMessageHandler = /* @__PURE__ */ __name((message, html, data) => {
  if (coloredBorders === "none") return true;
  let actorId = message.speaker.actor ?? "";
  let userId = message.author?.id ?? "";
  let actor = message.speakerActor;
  let user = game.users?.get(userId);
  if (actor) user = playerForActor(actor);
  if (!user) return true;
  html.style.borderColor = user.color.toString();
  const sender = html.querySelector(".message-sender");
  if (!sender) return;
  if (coloredBorders === "borderNamesBackground") {
    sender.style["text-shadow"] = `1px 1px 1px #FFFFFF`;
    sender.style.backgroundColor = user.color.toString();
  } else if (coloredBorders === "borderNamesText") {
    sender.style.color = user.color.toString();
    sender.style["text-shadow"] = `1px 1px 1px ${sender.style.color}`;
  }
  return true;
}, "colorChatMessageHandler");
function checkOverTimeSaves(message, data, options, user) {
  if (!message.rolls?.length || !["skill", "save", "ability"].includes(data.flags?.dnd5e?.roll?.type ?? "")) return true;
  let actor = message.speakerActor;
  if (message.speaker.token) {
    actor = game.scenes?.get(message.speaker?.scene ?? "")?.tokens?.get(message.speaker.token)?.actor ?? actor;
  }
  if (!actor) return true;
  const overtimeActorUuid = data.flags?.[MODULE_ID]?.overtimeActorUuid;
  if (actor.uuid !== overtimeActorUuid) {
    if (overtimeActorUuid) {
      const overTimeActor = fromUuidSync(overtimeActorUuid);
      ui.notifications?.warn(`Over time actor mismatch ${actor.name} should be ${overTimeActor?.name}`);
    }
    return true;
  }
  let activeCombatants = game.combats?.combats.map((combat) => combat.combatant?.token?.id);
  const isTurn = activeCombatants?.includes(ChatMessage.getSpeaker({ actor })?.token);
  const inCombat = isInCombat(actor);
  if (!isTurn && inCombat) {
    return true;
  }
  try {
    let func = /* @__PURE__ */ __name(async (actor2, rollFlags, roll) => {
      for (let effect of actor2.effects.filter((ef) => ef.changes.some((change) => change.key.startsWith("flags.midi-qol.OverTime")))) {
        await doOverTimeEffect(actor2, effect, true, { saveToUse: roll, rollFlags: data.flags?.dnd5e?.roll, isActionSave: true });
      }
    }, "func");
    func(actor, data.flags?.dnd5e?.roll, message.rolls[message.rolls.length - 1]);
  } catch (err) {
    const message2 = `checkOverTimeSaves error for ${actor?.name} ${actor.uuid}`;
    console.warn(message2, err);
    TroubleShooter.recordError(err, message2);
  } finally {
    return true;
  }
}
__name(checkOverTimeSaves, "checkOverTimeSaves");
var nsaMessageHandler = /* @__PURE__ */ __name((message, data, options, user) => {
  if (!nsaFlag || !message.whisper || message.whisper.length === 0) return true;
  let gmIds = ChatMessage.getWhisperRecipients("GM").filter((u) => u.active).map((u) => u.id);
  let currentIds = message.whisper;
  gmIds = gmIds.filter((id) => !currentIds.includes(id));
  if (debugEnabled > 1) debug("nsa handler active GMs ", gmIds, " current ids ", currentIds, "extra gmIds ", gmIds);
  if (gmIds.length > 0) message.updateSource({ "whisper": currentIds.concat(gmIds) });
  return true;
}, "nsaMessageHandler");
var hideRollRender = /* @__PURE__ */ __name((msg, html, data) => {
  if (forceHideRoll && (msg.whisper.length > 0 || msg?.blind)) {
    if (!game.user?.isGM && !msg.isAuthor && msg.whisper.indexOf(game.user?.id ?? "") === -1) {
      if (debugEnabled > 0) warn("hideRollRender | hiding message", msg.whisper);
      html.style.display = "none";
      setTimeout(() => {
        html.remove();
      }, 10);
    }
  }
  return true;
}, "hideRollRender");
var hideRollUpdate = /* @__PURE__ */ __name((message, data, options, id) => {
  return true;
}, "hideRollUpdate");
var hideStuffHandler = /* @__PURE__ */ __name((message, html, data) => {
  if (debugEnabled > 1) debug("hideStuffHandler message: ", message.id, message);
  if (forceHideRoll && message.blind && !game.user?.isGM || foundry.utils.getProperty(message, "flags.midi-qol.gmHide") && game.user?.isGM) {
    html.style.display = "none";
    return;
  }
  if (forceHideRoll && !(game.user?.isGM && nsaFlag) && message.whisper.length > 0 && !message.whisper.includes(game.user?.id ?? "") && !message.isAuthor) {
    html.style.display = "none";
    return;
  }
  if (!game.user?.isGM && message.blind) {
    html.querySelectorAll(".midi-attack-roll .dice-roll").forEach((el) => {
      el.outerHTML = `<span>${i18n("midi-qol.DiceRolled")}</span>`;
    });
    if (!(message.flags && message.flags["monks-tokenbar"]))
      html.querySelectorAll(".dice-roll").forEach((el) => {
        el.outerHTML = `<span>${i18n("midi-qol.DiceRolled")}</span>`;
      });
  }
  return true;
}, "hideStuffHandler");
function ddbglPendingFired(data) {
  let { sceneId, tokenId, actorId, itemId, actionType } = data;
  if (!itemId || !["attack", "damage", "heal"].includes(actionType)) {
    error2("DDB Game Log - no item/action for pending roll");
    return;
  }
  const token = fromUuidSync(`Scene.${sceneId ?? 0}.Token.${tokenId ?? 0}`)?.object;
  const actor = token?.actor ?? game.actors?.get(actorId ?? "");
  if (!actor) {
    warn(" ddb-game-log hook could not find actor");
    return;
  }
  let player;
  if (token) {
    player = playerFor(token);
  } else {
    player = game.users?.players.find((p) => p.active && actor?.testUserPermission(p, "OWNER"));
  }
  if (!player || !player.active) player = ChatMessage.getWhisperRecipients("GM").find((u) => u.active);
  if (player?.id !== game.user?.id) return;
  let item = actor.items.get(itemId);
  if (!item) {
    warn(` ddb-game-log - hook could not find item ${itemId} on actor ${actor.name}`);
    return;
  }
  let workflow = DDBGameLogWorkflow.get(item.uuid);
  if (actionType === "attack") {
    if (DDBGameLogWorkflow.get(item.uuid)) Workflow.removeWorkflow(item.uuid);
    workflow = void 0;
  }
  if (["damage", "heal"].includes(actionType) && item.hasAttack && !workflow) {
    warn(` ddb-game-log damage roll without workflow being started ${actor.name} using ${item.name}`);
    return;
  }
  if (!workflow) {
    const speaker = {
      scene: sceneId,
      token: tokenId,
      actor: actorId,
      alias: token?.name ?? actor.name
    };
    workflow = new DDBGameLogWorkflow(actor, item, speaker, Array.from(game.user?.targets ?? /* @__PURE__ */ new Set()), {});
    item.displayCard({ showFullCard: false, workflow, createMessage: false, defaultCard: true });
    return;
  }
}
__name(ddbglPendingFired, "ddbglPendingFired");
function ddbglPendingHook(data) {
  if (!configSettings.enableDDBGL) return;
  socketlibSocket.executeForEveryone("ddbglPendingFired", data);
}
__name(ddbglPendingHook, "ddbglPendingHook");
function processCreateDDBGLMessages(message, options, user) {
  if (!configSettings.enableDDBGL) return;
  const flags = message.flags;
  if (!flags || !flags["ddb-game-log"] || !game.user) return;
  const ddbGLFlags = flags["ddb-game-log"];
  if (!ddbGLFlags || ddbGLFlags.pending) return;
  if (!["attack", "damage", "heal"].includes(flags.dnd5e?.roll?.type ?? "")) return;
  const itemId = flags.dnd5e?.roll?.itemId;
  if (!itemId) {
    error2("Could not find item for fulfilled roll");
    return;
  }
  const token = fromUuidSync(`Scene.${message.speaker.scene}.Token.${message.speaker.token}`)?.object;
  const actor = token?.actor ?? game.actors?.get(message.speaker.actor ?? "");
  if (!actor) {
    error2("ddb-game-log could not find actor for roll");
    return;
  }
  let player;
  if (token) {
    player = playerFor(token);
  } else {
    player = game.users?.players.find((p) => p.active && actor?.testUserPermission(p, "OWNER"));
  }
  if (!player || !player.active) player = preferredActiveGM();
  if (player?.id !== game.user?.id) return;
  const item = actor.items.get(itemId);
  if (!item) {
    error2(`ddb-game-log roll could not find item ${flags?.dnd5e?.roll?.itemId} on actor ${actor.name}`);
    return;
  }
  let workflow = DDBGameLogWorkflow.get(item.uuid);
  if (!workflow && flags.dnd5e.roll.type === "damage" && item.hasAttack && ["rwak", "mwak"].includes(item.actionType)) {
    warn(`ddb-game-log roll damage roll without workflow being started ${actor.name} using ${item.name}`);
    return;
  }
  if (!workflow) {
    error2(`ddb-game-log roll no workflow for ${item.name}`);
    return;
  }
  if (configSettings.undoWorkflow && workflow.undoData && message) {
    if (!workflow.undoData.chatCardUuids) workflow.undoData.chatCardUuids = [];
    workflow.undoData.chatCardUuids = workflow.undoData.chatCardUuids.concat([message.uuid]);
    unTimedExecuteAsGM("updateUndoChatCardUuids", workflow.undoData);
  }
  if (flags?.dnd5e?.roll?.type === "attack") {
    let rolls = message.rolls;
    if (!(rolls instanceof Array)) rolls = [rolls];
    workflow.attackRoll = rolls[0] ?? void 0;
    workflow.attackTotal = rolls[0]?.total ?? 0;
    workflow.needsDamage = workflow.item.hasDamage;
    workflow.attackRollHTML = message.content;
    workflow.attackRolled = true;
    if (workflow.currentAction === workflow.WorkflowState_WaitForAttackRoll) {
      if (workflow.suspended) workflow.unSuspend({ attackRoll: workflow.attackRoll });
    }
  }
  if (["damage", "heal"].includes(flags.dnd5e?.roll?.type ?? "")) {
    let rolls = message.rolls;
    if (!rolls) return;
    workflow.attackRolled = true;
    if (!(rolls instanceof Array)) rolls = [rolls];
    if (workflow.needsDamage && rolls?.length) {
      workflow.needsDamage = false;
      workflow.setDamageRolls(rolls);
    } else if (workflow.needsOtherDamage && rolls?.length) {
      workflow.setOtherDamageRolls(rolls);
      workflow.needsOtherDamage = false;
    }
    if (workflow.currentAction === workflow.WorkflowState_WaitForDamageRoll) {
      if (workflow.suspended) workflow.unSuspend({ damageRoll: workflow.damageRoll });
    }
  }
}
__name(processCreateDDBGLMessages, "processCreateDDBGLMessages");

// src/module/apps/Item.ts
var OnUseMacros = class _OnUseMacros {
  static {
    __name(this, "OnUseMacros");
  }
  items;
  constructor(onUseMacros = null) {
    if (typeof onUseMacros === "string") {
      this.items = onUseMacros?.split(",")?.filter((value) => value.trim().length > 0)?.map((macro) => new OnUseMacro(macro));
    } else {
      this.items = [];
    }
  }
  static parseParts(parts) {
    const macros = new _OnUseMacros();
    parts.items?.forEach((x) => macros.items.push(OnUseMacro.parsePart(x)));
    return macros;
  }
  getMacros(currentOption) {
    return this.items.filter((x) => x.macroName?.length > 0 && (x.option.toLocaleLowerCase() === (currentOption ?? "").toLocaleLowerCase() || x.option === "all")).map((x) => x.macroName).toString();
  }
  toString() {
    return this.items.map((m) => m.toString()).join(",");
  }
};
var OnUseMacro = class _OnUseMacro {
  static {
    __name(this, "OnUseMacro");
  }
  macroName;
  option;
  macro = void 0;
  macroPassOptions = {};
  constructor(macro = void 0) {
    if (macro === void 0) {
      this.macroName = "ItemMacro";
    } else {
      const pattern = new RegExp("(?:\\[(?<option>.*?)\\])?(?<macroName>.*)", "");
      let data = macro.match(pattern)?.groups;
      this.macroName = data["macroName"].trim();
      this.option = data["option"];
    }
    this.option ??= "postActiveEffects";
  }
  static parsePart(parts) {
    const m = new _OnUseMacro();
    m.macroName = parts.macroName;
    m.option = parts.option ?? m.option;
    return m;
  }
  toString() {
    return `[${this.option}]${this.macroName}`;
  }
};
var OnUseMacroOptions = class {
  static {
    __name(this, "OnUseMacroOptions");
  }
  static options;
  static setOptions(options) {
    this.options = [];
    for (let option of Object.keys(options)) {
      this.options.push({ option, label: options[option] });
    }
  }
  static get getOptions() {
    return this.options;
  }
};
function getCurrentSourceMacros(document2) {
  const macroField = new OnUseMacros(foundry.utils.getProperty(document2, "_source.flags.midi-qol.onUseMacroName") ?? null);
  return macroField;
}
__name(getCurrentSourceMacros, "getCurrentSourceMacros");

// src/module/lib/RollModifierTracker.ts
function midiFlag(flag) {
  return `flags.midi-qol.${flag}`;
}
__name(midiFlag, "midiFlag");
function resolveKeyLabel(key) {
  if (key === "all") return "All";
  const abilityLabel = GameSystemConfig.abilities?.[key]?.label;
  if (abilityLabel) return abilityLabel;
  const skillLabel = GameSystemConfig.skills?.[key]?.label;
  if (skillLabel) return skillLabel;
  const actionLabel = GameSystemConfig.itemActionTypes?.[key];
  if (actionLabel) return actionLabel;
  const dmgLabel = GameSystemConfig.damageTypes?.[key]?.label;
  if (dmgLabel) return dmgLabel;
  const toolLabel = globalThis.dnd5e?.documents?.Trait?.keyLabel?.(`tool:${key}`);
  if (toolLabel) return toolLabel;
  return key;
}
__name(resolveKeyLabel, "resolveKeyLabel");
function getFlagDisplayName(flag) {
  const lmEntry = globalThis.DAE?.localizationMap?.[midiFlag(flag)];
  if (lmEntry?.name) return lmEntry.name;
  if (!game.i18n) return flag;
  const exactKey = `midi-qol.flagTemplate.${flag}.name`;
  const exactResult = game.i18n.localize(exactKey);
  if (exactResult !== exactKey) {
    if (exactResult.includes("{label}")) return exactResult.replace(/\s*\(\{label\}\)/, "");
    return exactResult;
  }
  const lastDot = flag.lastIndexOf(".");
  if (lastDot > 0) {
    const prefix = flag.substring(0, lastDot);
    const lastElement = flag.substring(lastDot + 1);
    const prefixKey = `midi-qol.flagTemplate.${prefix}.name`;
    const prefixResult = game.i18n.localize(prefixKey);
    if (prefixResult !== prefixKey) {
      const label = resolveKeyLabel(lastElement);
      return i18nFormat2(prefixKey, { label });
    }
  }
  return flag;
}
__name(getFlagDisplayName, "getFlagDisplayName");
function getAttributionDisplayName(actor, flag, flagDisplayName) {
  const evalKey = `flags.midi-qol.${flag}`.replace("flags.midi-qol.", "flags.midi.evaluated.");
  const result = foundry.utils.getProperty(actor, `${evalKey}.effects`);
  const effectNames = Array.isArray(result) ? result : [];
  const realEffects = effectNames.filter((name) => name !== "flag");
  if (realEffects.length > 0) {
    const uniqueNames = [...new Set(realEffects)];
    return `${uniqueNames.join(", ")} - ${flagDisplayName}`;
  }
  return flagDisplayName;
}
__name(getAttributionDisplayName, "getAttributionDisplayName");
var AdvantageDisadvantageTracker = class {
  static {
    __name(this, "AdvantageDisadvantageTracker");
  }
  #active = false;
  #suppressed = false;
  #override = false;
  #type;
  #suppressionType;
  #addAttribution;
  constructor(type, addAttribution2) {
    this.#type = type;
    this.#suppressionType = type === "ADV" ? "NOADV" : "NODIS";
    this.#addAttribution = addAttribution2;
  }
  // ─────────────────────────────────────────────────────────────────
  // State Inspection
  // ─────────────────────────────────────────────────────────────────
  /** Get complete state snapshot */
  get get() {
    return { active: this.#active, suppressed: this.#suppressed, override: this.#override };
  }
  // ─────────────────────────────────────────────────────────────────
  // Query Methods
  // ─────────────────────────────────────────────────────────────────
  /** Whether at least one source has been added */
  get isAdded() {
    return this.#active;
  }
  /** Whether suppressed by a blocking flag */
  get isSuppressed() {
    return this.#suppressed;
  }
  /** Whether override is set (bypasses suppression) */
  get hasOverride() {
    return this.#override;
  }
  /**
   * Computed final state.
   * Returns true if active and not suppressed (or if override is set).
   * Returns undefined if not active or if suppressed.
   */
  get isActive() {
    if (this.#override) return this.#active || void 0;
    if (this.#suppressed) return void 0;
    return this.#active || void 0;
  }
  // ─────────────────────────────────────────────────────────────────
  // Mutation Methods
  // ─────────────────────────────────────────────────────────────────
  /**
   * Add a source of advantage/disadvantage.
   * @param source - Optional source identifier for attribution
   * @param displayName - Optional human-readable display name
   */
  add(source, displayName) {
    this.#active = true;
    if (source !== void 0) {
      this.#addAttribution(this.#type, source, displayName ?? source);
    }
  }
  /**
   * Clear active state.
   * Preserves suppression state.
   */
  clear() {
    this.#active = false;
  }
  /**
   * Suppress this modifier.
   * When suppressed, isActive returns undefined.
   * @param source - Optional source identifier for attribution
   * @param displayName - Optional human-readable display name
   */
  suppress(source, displayName) {
    this.#suppressed = true;
    if (source !== void 0) {
      this.#addAttribution(this.#suppressionType, source, displayName ?? source);
    }
  }
  /**
   * Force this modifier, bypassing suppression.
   * @param source - Optional source identifier for attribution
   * @param displayName - Optional human-readable display name
   */
  setOverride(source, displayName) {
    this.#active = true;
    this.#override = true;
    if (source !== void 0) {
      this.#addAttribution(this.#type, source, displayName ?? source);
    }
  }
  /**
   * Clear override state.
   */
  clearOverride() {
    this.#override = false;
  }
  /**
   * Reset all state to initial values.
   */
  reset() {
    this.#active = false;
    this.#suppressed = false;
    this.#override = false;
  }
  /** Restore state from serialized data */
  restore(state) {
    this.#active = state.active;
    this.#suppressed = state.suppressed;
    this.#override = state.override;
  }
};
var CriticalFumbleTracker = class {
  static {
    __name(this, "CriticalFumbleTracker");
  }
  #natural = false;
  #forced = false;
  #suppressed = false;
  #override = void 0;
  #type;
  #noType;
  #addAttribution;
  constructor(type, addAttribution2) {
    this.#type = type;
    this.#noType = type === "CRIT" ? "NOCRIT" : "NOFUMBLE";
    this.#addAttribution = addAttribution2;
  }
  // ─────────────────────────────────────────────────────────────────
  // State Inspection
  // ─────────────────────────────────────────────────────────────────
  /** Get complete state snapshot */
  get get() {
    return {
      natural: this.#natural,
      forced: this.#forced,
      suppressed: this.#suppressed,
      override: this.#override
    };
  }
  // ─────────────────────────────────────────────────────────────────
  // Query Methods
  // ─────────────────────────────────────────────────────────────────
  /** Whether natural crit/fumble occurred from dice */
  get isNatural() {
    return this.#natural;
  }
  /** Whether forced by a flag */
  get isForced() {
    return this.#forced;
  }
  /** Whether suppressed by a blocking flag */
  get isSuppressed() {
    return this.#suppressed;
  }
  /** Whether override is set */
  get hasOverride() {
    return this.#override !== void 0;
  }
  /** Get override value (undefined if not set) */
  get overrideValue() {
    return this.#override;
  }
  /**
   * Computed final state.
   * Priority: override > suppressed > (natural || forced)
   */
  get isActive() {
    if (this.#override !== void 0) return this.#override;
    if (this.#suppressed) return false;
    return this.#natural || this.#forced;
  }
  // ─────────────────────────────────────────────────────────────────
  // Mutation Methods
  // ─────────────────────────────────────────────────────────────────
  /**
   * Set natural crit/fumble from dice roll.
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  setNatural(source, displayName) {
    this.#natural = true;
    this.#addAttribution(this.#type, source, displayName ?? source);
  }
  /**
   * Force crit/fumble from a flag.
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  force(source, displayName) {
    this.#forced = true;
    this.#addAttribution(this.#type, source, displayName ?? source);
  }
  /**
   * Suppress (prevent) crit/fumble.
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  suppress(source, displayName) {
    this.#suppressed = true;
    this.#addAttribution(this.#noType, source, displayName ?? source);
  }
  /**
   * Override crit/fumble state absolutely.
   * @param value - true to force, false to prevent
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  setOverride(value, source, displayName) {
    this.#override = value;
    this.#addAttribution(value ? this.#type : this.#noType, source, displayName ?? source);
  }
  /**
   * Reset all state to initial values.
   */
  reset() {
    this.#natural = false;
    this.#forced = false;
    this.#suppressed = false;
    this.#override = void 0;
  }
  /** Restore state from serialized data */
  restore(state) {
    this.#natural = state.natural;
    this.#forced = state.forced;
    this.#suppressed = state.suppressed;
    this.#override = state.override;
  }
};
var RollValueTracker = class {
  static {
    __name(this, "RollValueTracker");
  }
  #autoFail = false;
  #autoSuccess = false;
  #min = -Infinity;
  #max = Infinity;
  #addAttribution;
  constructor(addAttribution2) {
    this.#addAttribution = addAttribution2;
  }
  // ─────────────────────────────────────────────────────────────────
  // State Inspection
  // ─────────────────────────────────────────────────────────────────
  /** Get complete state snapshot */
  get get() {
    return {
      willFail: this.#autoFail,
      willSucceed: this.#autoSuccess,
      minValue: this.#min,
      maxValue: this.#max
    };
  }
  // ─────────────────────────────────────────────────────────────────
  // Query Methods
  // ─────────────────────────────────────────────────────────────────
  get willFail() {
    return this.#autoFail;
  }
  get willSucceed() {
    return this.#autoSuccess;
  }
  get minValue() {
    return this.#min;
  }
  get maxValue() {
    return this.#max;
  }
  get hasMin() {
    return this.#min > -Infinity;
  }
  get hasMax() {
    return this.#max < Infinity;
  }
  // ─────────────────────────────────────────────────────────────────
  // Mutation Methods
  // ─────────────────────────────────────────────────────────────────
  /**
   * Set auto-fail for this roll.
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  fail(source, displayName) {
    this.#autoFail = true;
    this.#addAttribution("FAIL", source, displayName ?? source);
  }
  /**
   * Set auto-success for this roll.
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  succeed(source, displayName) {
    this.#autoSuccess = true;
    this.#addAttribution("SUCCESS", source, displayName ?? source);
  }
  /**
   * Set minimum roll value (only if higher than current).
   * @param value - Minimum roll value
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  min(value, source, displayName) {
    if (value > this.#min) {
      this.#min = value;
      this.#addAttribution("MIN", source, displayName ?? `${source} (${value})`);
    }
  }
  /**
   * Force set minimum roll value, ignoring current value.
   * @param value - Minimum roll value
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  forceMin(value, source, displayName) {
    this.#min = value;
    this.#addAttribution("MIN", source, displayName ?? `${source} (${value})`);
  }
  /**
   * Set maximum roll value (only if lower than current).
   * @param value - Maximum roll value
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  max(value, source, displayName) {
    if (value < this.#max) {
      this.#max = value;
      this.#addAttribution("MAX", source, displayName ?? `${source} (${value})`);
    }
  }
  /**
   * Force set maximum roll value, ignoring current value.
   * @param value - Maximum roll value
   * @param source - Source identifier for attribution
   * @param displayName - Human-readable display name
   */
  forceMax(value, source, displayName) {
    this.#max = value;
    this.#addAttribution("MAX", source, displayName ?? `${source} (${value})`);
  }
  /**
   * Reset all state to initial values.
   */
  reset() {
    this.#autoFail = false;
    this.#autoSuccess = false;
    this.#min = -Infinity;
    this.#max = Infinity;
  }
  /** Restore state from serialized data */
  restore(state) {
    this.#autoFail = state.willFail;
    this.#autoSuccess = state.willSucceed;
    this.#min = state.minValue;
    this.#max = state.maxValue;
  }
};
var RollModifierTracker = class {
  static {
    __name(this, "RollModifierTracker");
  }
  // ─────────────────────────────────────────────────────────────────
  // Sub-trackers
  // ─────────────────────────────────────────────────────────────────
  advantage;
  disadvantage;
  critical;
  fumble;
  modifiers;
  // ─────────────────────────────────────────────────────────────────
  // Attribution
  // ─────────────────────────────────────────────────────────────────
  #attribution = {};
  /** Legacy Set format: "${type}:${source}" */
  #legacyAttribution = /* @__PURE__ */ new Set();
  /** Legacy Set format for adv-reminder: "${type}:${source}" */
  #advReminderAttribution = /* @__PURE__ */ new Set();
  constructor() {
    const addAttr = this.addAttribution.bind(this);
    this.advantage = new AdvantageDisadvantageTracker("ADV", addAttr);
    this.disadvantage = new AdvantageDisadvantageTracker("DIS", addAttr);
    this.critical = new CriticalFumbleTracker("CRIT", addAttr);
    this.fumble = new CriticalFumbleTracker("FUMBLE", addAttr);
    this.modifiers = new RollValueTracker(addAttr);
  }
  // ─────────────────────────────────────────────────────────────────
  // Attribution Methods
  // ─────────────────────────────────────────────────────────────────
  /**
   * Add an attribution entry.
   * @param type - Attribution type (ADV, DIS, NOADV, NODIS, CRIT, etc.)
   * @param source - Source identifier (e.g., 'flanking', 'invisible')
   * @param displayName - Human-readable display name
   */
  addAttribution(type, source, displayName) {
    this.#attribution[type] ??= {};
    this.#attribution[type][source] = displayName;
    this.#legacyAttribution.add(`${type}:${source}`);
    this.#advReminderAttribution.add(`${type}:${source}`);
  }
  /** Get the attribution map */
  get attribution() {
    return this.#attribution;
  }
  /** Get legacy Set format: "${type}:${displayName}" */
  get legacyAttribution() {
    return this.#legacyAttribution;
  }
  /** Get legacy Set format for adv-reminder: "${type}:${source}" */
  get advReminderAttribution() {
    return this.#advReminderAttribution;
  }
  // ─────────────────────────────────────────────────────────────────
  // Combined Mode
  // ─────────────────────────────────────────────────────────────────
  /**
   * Get the combined advantage/disadvantage mode.
   * @returns ADV_MODE.ADVANTAGE, ADV_MODE.DISADVANTAGE, or ADV_MODE.NORMAL
   */
  get advantageMode() {
    const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
    if (this.advantage.hasOverride && !this.disadvantage.hasOverride) return ADV_MODE.ADVANTAGE;
    if (this.disadvantage.hasOverride && !this.advantage.hasOverride) return ADV_MODE.DISADVANTAGE;
    if (this.advantage.hasOverride && this.disadvantage.hasOverride) return ADV_MODE.NORMAL;
    if (this.advantage.isActive && !this.disadvantage.isActive) return ADV_MODE.ADVANTAGE;
    if (this.disadvantage.isActive && !this.advantage.isActive) return ADV_MODE.DISADVANTAGE;
    return ADV_MODE.NORMAL;
  }
  // ─────────────────────────────────────────────────────────────────
  // Convenience Getters (delegate to sub-trackers)
  // ─────────────────────────────────────────────────────────────────
  /** Check if there's effective advantage */
  get hasAdvantage() {
    return this.advantage.isActive;
  }
  /** Check if there's effective disadvantage */
  get hasDisadvantage() {
    return this.disadvantage.isActive;
  }
  /** Computed final critical state */
  get isCritical() {
    return this.critical.isActive;
  }
  /** Computed final fumble state */
  get isFumble() {
    return this.fumble.isActive;
  }
  // ─────────────────────────────────────────────────────────────────
  // Utility Methods
  // ─────────────────────────────────────────────────────────────────
  /**
   * Reset all state to initial values.
   */
  reset() {
    this.advantage.reset();
    this.disadvantage.reset();
    this.critical.reset();
    this.fumble.reset();
    this.modifiers.reset();
    this.#attribution = {};
    this.#legacyAttribution.clear();
    this.#advReminderAttribution.clear();
  }
  // ─────────────────────────────────────────────────────────────────
  // Serialization
  // ─────────────────────────────────────────────────────────────────
  /** Serialize tracker state to a plain object for persistence */
  toJSON() {
    return {
      advantage: this.advantage.get,
      disadvantage: this.disadvantage.get,
      critical: this.critical.get,
      fumble: this.fumble.get,
      modifiers: this.modifiers.get,
      attribution: this.#attribution,
      legacyAttribution: Array.from(this.#legacyAttribution),
      advReminderAttribution: Array.from(this.#advReminderAttribution)
    };
  }
  /** Restore tracker state from serialized data */
  restore(data) {
    if (!data) return;
    if (data.advantage) this.advantage.restore(data.advantage);
    if (data.disadvantage) this.disadvantage.restore(data.disadvantage);
    if (data.critical) this.critical.restore(data.critical);
    if (data.fumble) this.fumble.restore(data.fumble);
    if (data.modifiers) this.modifiers.restore(data.modifiers);
    if (data.attribution) this.#attribution = data.attribution;
    if (data.legacyAttribution) this.#legacyAttribution = new Set(data.legacyAttribution);
    if (data.advReminderAttribution) this.#advReminderAttribution = new Set(data.advReminderAttribution);
  }
  processKeys(config = {}) {
    if (!config?.event) return;
    const areKeysPressed = game.system.utils.areKeysPressed;
    const keys = {
      normal: areKeysPressed(config.event, "skipDialogNormal"),
      advantage: areKeysPressed(config.event, "skipDialogAdvantage"),
      disadvantage: areKeysPressed(config.event, "skipDialogDisadvantage")
    };
    if (keys.advantage && keys.disadvantage && keys.normal) {
      this.advantage.setOverride("forcedKeyPress", i18n("midi-qol.ForcedKeyPress"));
      this.disadvantage.setOverride("forcedKeyPress", i18n("midi-qol.ForcedKeyPress"));
      config.advantage = false;
      config.disadvantage = false;
      config.event = void 0;
    } else {
      if (keys.disadvantage) {
        const source = keys.normal ? "forcedKeyPress" : "keyPress";
        if (keys.normal)
          this.disadvantage.setOverride(source, i18n("midi-qol.KeyPress"));
        else
          this.disadvantage.add(source, i18n("midi-qol.KeyPress"));
      }
      if (keys.advantage) {
        const source = keys.normal ? "forcedKeyPress" : "keyPress";
        if (keys.normal)
          this.advantage.setOverride(source, i18n("midi-qol.ForcedKeyPress"));
        else
          this.advantage.add(source, i18n("midi-qol.KeyPress"));
      }
    }
  }
};
async function computeAdvantageFlags(actor, flags, options, tracker) {
  tracker ??= new RollModifierTracker();
  if (options.advantage) {
    tracker.advantage.add("options", "Options");
  }
  if (options.disadvantage) {
    tracker.disadvantage.add("options", "Options");
  }
  const grantsActor = options.grantsActor ?? options.workflow?.actor;
  const grantsToken = options.grantsToken ?? (grantsActor ? tokenForActor(grantsActor) : void 0);
  const conditionData = createConditionData({
    workflow: options.workflow,
    target: grantsToken ?? tokenForActor(actor),
    actor,
    item: options.item ?? options.itemUuid ?? options.saveItem ?? options.saveItemUuid
  });
  for (const flag of flags.advantageFlags) {
    if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
      const source = flag.replace(/^advantage\./, "");
      tracker.advantage.add(source, getAttributionDisplayName(actor, flag, getFlagDisplayName(flag)));
      break;
    }
  }
  for (const flag of flags.disadvantageFlags) {
    if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
      const source = flag.replace(/^disadvantage\./, "");
      tracker.disadvantage.add(source, getAttributionDisplayName(actor, flag, getFlagDisplayName(flag)));
      break;
    }
  }
  if (flags.noAdvantageFlags?.length) {
    for (const flag of flags.noAdvantageFlags) {
      if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
        tracker.advantage.suppress(flag, getAttributionDisplayName(actor, flag, getFlagDisplayName(flag)));
        break;
      }
    }
  }
  if (flags.noDisadvantageFlags?.length) {
    for (const flag of flags.noDisadvantageFlags) {
      if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
        tracker.disadvantage.suppress(flag, getAttributionDisplayName(actor, flag, getFlagDisplayName(flag)));
        break;
      }
    }
  }
  if (grantsActor) {
    const grantsConditionData = createConditionData({
      workflow: options.workflow,
      target: tokenForActor(actor),
      actor: grantsActor,
      token: grantsToken,
      item: options.item ?? options.itemUuid ?? options.saveItem ?? options.saveItemUuid
    });
    const grantsName = grantsToken?.name ?? grantsActor.name ?? "";
    if (flags.sourceAdvantageFlags?.length) {
      for (const flag of flags.sourceAdvantageFlags) {
        if (await evalAllConditionsAsync(grantsActor, midiFlag(flag), grantsConditionData, false)) {
          const effectDisplay = getAttributionDisplayName(grantsActor, flag, getFlagDisplayName(flag));
          tracker.advantage.add(`${flag} ${grantsName}`, `${effectDisplay} (${grantsName})`);
          break;
        }
      }
    }
    if (flags.sourceDisadvantageFlags?.length) {
      for (const flag of flags.sourceDisadvantageFlags) {
        if (await evalAllConditionsAsync(grantsActor, midiFlag(flag), grantsConditionData, false)) {
          const effectDisplay = getAttributionDisplayName(grantsActor, flag, getFlagDisplayName(flag));
          tracker.disadvantage.add(`${flag} ${grantsName}`, `${effectDisplay} (${grantsName})`);
          break;
        }
      }
    }
    if (flags.sourceNoAdvantageFlags?.length) {
      for (const flag of flags.sourceNoAdvantageFlags) {
        if (await evalAllConditionsAsync(grantsActor, midiFlag(flag), grantsConditionData, false)) {
          const effectDisplay = getAttributionDisplayName(grantsActor, flag, getFlagDisplayName(flag));
          tracker.advantage.suppress(`${flag} ${grantsName}`, `${effectDisplay} (${grantsName})`);
          break;
        }
      }
    }
    if (flags.sourceNoDisadvantageFlags?.length) {
      for (const flag of flags.sourceNoDisadvantageFlags) {
        if (await evalAllConditionsAsync(grantsActor, midiFlag(flag), grantsConditionData, false)) {
          const effectDisplay = getAttributionDisplayName(grantsActor, flag, getFlagDisplayName(flag));
          tracker.disadvantage.suppress(`${flag} ${grantsName}`, `${effectDisplay} (${grantsName})`);
          break;
        }
      }
    }
  }
  if (flags.systemRollModeFields?.length) {
    for (const field of flags.systemRollModeFields) {
      const rollMode = foundry.utils.getProperty(actor.system, field);
      if (rollMode === 1) {
        tracker.advantage.add("system.rollMode", "System Roll Mode");
      } else if (rollMode === -1) {
        tracker.disadvantage.add("system.rollMode", "System Roll Mode");
      }
    }
  }
  return tracker;
}
__name(computeAdvantageFlags, "computeAdvantageFlags");
function computeRollModifiers(actor, flags, tracker, options) {
  const conditionData = createConditionData({ workflow: options?.workflow, actor, target: tokenForActor(actor) });
  for (const flag of flags.failFlags) {
    if (evalAllConditions(actor, midiFlag(flag), conditionData, false)) {
      tracker.modifiers.fail(flag, getFlagDisplayName(flag));
      break;
    }
  }
  for (const flag of flags.successFlags) {
    if (evalAllConditions(actor, midiFlag(flag), conditionData, false)) {
      tracker.modifiers.succeed(flag, getFlagDisplayName(flag));
      break;
    }
  }
  for (const flag of flags.maxFlags) {
    const value = evalAllConditions(actor, midiFlag(flag), conditionData, Infinity);
    if (typeof value !== "boolean" && Number.isNumeric(value)) {
      const numValue = Number(value);
      tracker.modifiers.max(numValue, getFlagDisplayName(flag));
    }
  }
  for (const flag of flags.minFlags) {
    const value = evalAllConditions(actor, midiFlag(flag), conditionData, -Infinity);
    if (typeof value !== "boolean" && Number.isNumeric(value)) {
      const numValue = Number(value);
      tracker.modifiers.min(numValue, getFlagDisplayName(flag));
    }
  }
}
__name(computeRollModifiers, "computeRollModifiers");
function applyRollModifiers(tracker, config, dialog, options) {
  const failValue = options?.failValue ?? 1;
  const successValue = options?.successValue ?? 99;
  const autoFail = tracker.modifiers.willFail;
  const autoSuccess = tracker.modifiers.willSucceed;
  const max = tracker.modifiers.maxValue;
  const min = tracker.modifiers.minValue;
  const result = {
    autoFail,
    autoSuccess,
    max: max === Infinity ? void 0 : max,
    min: min === -Infinity ? void 0 : min
  };
  config.rolls ??= [];
  if (!config.rolls.length) {
    config.rolls.push({ options: {} });
  }
  if (autoFail) {
    dialog.configure = false;
    config.rolls = [{ options: { maximum: failValue }, parts: [] }];
  } else if (autoSuccess) {
    dialog.configure = false;
    config.rolls = [{ options: { minimum: successValue }, parts: [] }];
  } else {
    for (const roll of config.rolls) {
      roll.options ??= {};
      if (max < Infinity) {
        roll.options.maximum = Math.min(roll.options.maximum ?? Infinity, max);
      }
      if (min > -Infinity) {
        roll.options.minimum = Math.max(roll.options.minimum ?? -Infinity, min);
      }
    }
  }
  return result;
}
__name(applyRollModifiers, "applyRollModifiers");
async function computeCriticalModifiers(actor, flags, tracker, options) {
  const conditionData = createConditionData({ workflow: options?.workflow, actor, target: tokenForActor(actor) });
  for (const flag of flags.criticalFlags) {
    if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
      tracker.critical.force(flag, getFlagDisplayName(flag));
      break;
    }
  }
  for (const flag of flags.noCriticalFlags) {
    if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
      tracker.critical.suppress(flag, getFlagDisplayName(flag));
      break;
    }
  }
  for (const flag of flags.fumbleFlags) {
    if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
      tracker.fumble.force(flag, getFlagDisplayName(flag));
      break;
    }
  }
  for (const flag of flags.noFumbleFlags) {
    if (await evalAllConditionsAsync(actor, midiFlag(flag), conditionData, false)) {
      tracker.fumble.suppress(flag, getFlagDisplayName(flag));
      break;
    }
  }
}
__name(computeCriticalModifiers, "computeCriticalModifiers");
async function computeTargetCriticalModifiers(targetActor, targetName, flags, tracker, options) {
  const conditionData = createConditionData({
    workflow: options?.workflow,
    actor: options?.grantsActor,
    target: tokenForActor(targetActor)
  });
  for (const flag of flags.criticalFlags) {
    if (await evalAllConditionsAsync(targetActor, midiFlag(flag), conditionData, false)) {
      tracker.critical.force(flag, `${getFlagDisplayName(flag)} (${targetName})`);
      break;
    }
  }
  for (const flag of flags.noCriticalFlags) {
    if (await evalAllConditionsAsync(targetActor, midiFlag(flag), conditionData, false)) {
      tracker.critical.suppress(flag, `${getFlagDisplayName(flag)} (${targetName})`);
      break;
    }
  }
  for (const flag of flags.fumbleFlags) {
    if (await evalAllConditionsAsync(targetActor, midiFlag(flag), conditionData, false)) {
      tracker.fumble.force(flag, `${getFlagDisplayName(flag)} (${targetName})`);
      break;
    }
  }
  for (const flag of flags.noFumbleFlags) {
    if (await evalAllConditionsAsync(targetActor, midiFlag(flag), conditionData, false)) {
      tracker.fumble.suppress(flag, `${getFlagDisplayName(flag)} (${targetName})`);
      break;
    }
  }
}
__name(computeTargetCriticalModifiers, "computeTargetCriticalModifiers");

// src/module/apps/ConditionEditor.ts
var { ApplicationV2: ApplicationV28, HandlebarsApplicationMixin: HandlebarsApplicationMixin8 } = foundry.applications.api;
function buildConditionCompletions(api) {
  const completions = {};
  const tree = api.tree;
  function cloneWithExtras(treeEntry, overrides, extraChildren) {
    if (!treeEntry) return overrides ?? {};
    const entry = { ...treeEntry, ...overrides };
    if (extraChildren) {
      entry.children = { ...treeEntry.children ?? {}, ...extraChildren };
    }
    return entry;
  }
  __name(cloneWithExtras, "cloneWithExtras");
  const actorSystemChildren = tree.actor?.children?.system?.children ?? {};
  completions.isAttuned = { type: "property", detail: "boolean", info: "Whether the triggering item is attuned" };
  completions.options = { type: "property", detail: "object", info: "Roll options passed to the evaluation" };
  completions.isConcentrationCheck = { type: "property", detail: "boolean", info: "Whether this is a concentration check" };
  completions.isDeathSave = { type: "property", detail: "boolean", info: "Whether this is a death saving throw" };
  completions.actor = {
    type: "property",
    detail: "object",
    info: "Actor sub-object (raceOrType, typeOrRace, isCombatTurn only)",
    children: {
      raceOrType: { type: "property", detail: "string", info: "Race (PCs) or creature type (NPCs)" },
      typeOrRace: { type: "property", detail: "string", info: "Creature type (NPCs) or race (PCs)" },
      isCombatTurn: { type: "property", detail: "boolean", info: "Whether it's this actor's combat turn" }
    }
  };
  completions.items = { type: "property", detail: "array", info: "All actor item roll data (array)" };
  completions.equippedItems = { type: "property", detail: "array", info: "Equipped item roll data (array)" };
  completions.worldTime = { type: "property", detail: "number", info: "Current world time (game.time.worldTime)" };
  const actorSystemEntry = tree.actor?.children?.system;
  completions.target = {
    type: "property",
    detail: "object",
    info: "Target data (getRollData spread + condition fields)",
    children: {
      ...actorSystemChildren,
      system: actorSystemEntry ? cloneWithExtras(actorSystemEntry, { info: "Target actor's system data (getter)" }) : { type: "property", detail: "object", info: "Target actor's system data (getter)" },
      raceOrType: { type: "property", detail: "string", info: "Target's race or creature type" },
      typeOrRace: { type: "property", detail: "string", info: "Target's creature type or race" },
      items: { type: "property", detail: "array", info: "Target's item roll data" },
      equippedItems: { type: "property", detail: "array", info: "Target's equipped item roll data" },
      saved: { type: "property", detail: "boolean", info: "Whether target passed the save" },
      failedSave: { type: "property", detail: "boolean", info: "Whether target failed the save" },
      superSaver: { type: "property", detail: "boolean", info: "Whether target is a super saver" },
      semiSuperSaver: { type: "property", detail: "boolean", info: "Whether target is a semi-super saver" },
      isHit: { type: "property", detail: "boolean", info: "Whether target was hit by the attack" },
      isHitEC: { type: "property", detail: "boolean", info: "Whether target was hit (EC)" },
      canSense: { type: "property", detail: "boolean", info: "Whether target can sense the attacker" },
      canSee: { type: "property", detail: "boolean", info: "Whether target can see the attacker" },
      isCombatTurn: { type: "property", detail: "boolean", info: "Whether it's the target's combat turn" }
    }
  };
  completions.targetUuid = { type: "property", detail: "string", info: "Target token document UUID" };
  completions.targetId = { type: "property", detail: "string", info: "Target token ID" };
  completions.targetActorUuid = { type: "property", detail: "string", info: "Target actor UUID" };
  completions.targetActorId = { type: "property", detail: "string", info: "Target actor ID" };
  completions.raceOrType = { type: "property", detail: "string", info: "Target's race or creature type" };
  completions.typeOrRace = { type: "property", detail: "string", info: "Target's creature type or race" };
  completions.canSense = { type: "property", detail: "boolean", info: "Whether actor can sense the target" };
  completions.canSee = { type: "property", detail: "boolean", info: "Whether actor can see the target" };
  completions.humanoid = { type: "property", detail: "string[]", info: "Array of humanoid race/type strings" };
  completions.tokenUuid = { type: "property", detail: "string", info: "Actor's token document UUID" };
  completions.tokenId = { type: "property", detail: "string", info: "Actor's token ID (deprecated)" };
  completions.workflow = cloneWithExtras(
    tree.workflow,
    { info: "The active midi-qol Workflow instance" }
  );
  completions.w = cloneWithExtras(
    tree.workflow,
    { detail: "Workflow", info: "Alias for workflow" }
  );
  completions.activity = cloneWithExtras(
    tree.activity,
    { info: "The workflow's triggering activity" }
  );
  completions.a = cloneWithExtras(
    tree.activity,
    { detail: "Activity", info: "Alias for activity" }
  );
  completions.otherDamageActivity = cloneWithExtras(
    tree.activity,
    { detail: "Activity", info: "Other/versatile damage activity" }
  );
  completions.riderStatuses = { type: "property", detail: "object", info: "Status effects from applied effect riders ({status: true})" };
  completions.hasSave = { type: "property", detail: "boolean", info: "Whether the workflow includes a save" };
  completions.shouldRollDamage = { type: "property", detail: "boolean", info: "Whether damage should be rolled" };
  completions.hasAttack = { type: "property", detail: "boolean", info: "Whether the activity has an attack roll" };
  completions.hasDamage = { type: "property", detail: "boolean", info: "Whether the activity deals damage" };
  const itemSystemChildren = tree.item?.children?.system?.children ?? {};
  completions.item = {
    type: "property",
    detail: "object",
    info: "Item roll data (from getRollData().item, not the full Item document)",
    children: {
      ...itemSystemChildren,
      actionType: { type: "property", detail: "string", info: "Activity action type (mwak, rsak, save, etc.)" }
    }
  };
  completions.damageTypes = { type: "property", detail: "object", info: "Damage types from the roll ({type: true})" };
  completions.defaultDamageType = { type: "property", detail: "object", info: "Default damage type from the roll" };
  completions.combatRound = { type: "property", detail: "number", info: "Current combat round number" };
  completions.combatTurn = { type: "property", detail: "number", info: "Current combat turn number" };
  completions.combatTime = { type: "property", detail: "number", info: "Combat time (round + turn/100)" };
  completions.isCombatTurn = { type: "property", detail: "boolean", info: "Whether it's the actor's combat turn" };
  if (tree.CONFIG) completions.CONFIG = cloneWithExtras(tree.CONFIG);
  if (tree.CONST) completions.CONST = cloneWithExtras(tree.CONST);
  const midiChildren = tree.MidiQOL?.children ?? {};
  completions.Roll = cloneWithExtras(
    tree.Roll,
    { info: "Foundry Roll class for dice expressions" }
  );
  for (const fn of [
    "findNearby",
    "findNearbyCount",
    "checkNearby",
    "hasCondition",
    "checkIncapacitated",
    "canSee",
    "canSense",
    "computeDistance",
    "checkActivityRange",
    "checkDistance",
    "contestedRoll",
    "sizeNumeric"
  ]) {
    if (midiChildren[fn]) completions[fn] = midiChildren[fn];
  }
  completions.checkDefeated = {
    type: "function",
    detail: "(actorRef) => 0 | 1",
    info: "Check if actor/token is defeated.\nReturns: 1 if defeated, 0 if not"
  };
  completions.fromUuidSync = {
    type: "function",
    detail: "(uuid) => Document | null",
    info: "Synchronously resolve a UUID to a Document"
  };
  completions.confirm = {
    type: "function",
    detail: "(message) => Promise<boolean>",
    info: "Show a confirmation dialog (async only)"
  };
  completions.nonWorkflowTargetedToken = {
    type: "property",
    detail: "string",
    info: "UUID of the first user-targeted token (outside workflow context)"
  };
  completions.combat = cloneWithExtras(
    tree.game?.children?.combat ?? tree.combat,
    { detail: "Combat", info: "Active combat encounter (game.combat)" }
  );
  completions.evalRaceOrType = {
    type: "function",
    detail: "(actor) => string",
    info: "Get race (PCs) or creature type (NPCs) of an actor"
  };
  completions.evalTypeOrRace = {
    type: "function",
    detail: "(actor) => string",
    info: "Get creature type (NPCs) or race (PCs) of an actor"
  };
  if (actorSystemChildren) {
    for (const [key, value] of Object.entries(actorSystemChildren)) {
      if (!completions[key]) {
        completions[key] = value;
      }
    }
  }
  return completions;
}
__name(buildConditionCompletions, "buildConditionCompletions");
function buildBlocklyCategories(api) {
  const midiChildren = api.tree?.MidiQOL?.children ?? {};
  const actorSystemChildren = api.tree?.actor?.children?.system?.children ?? {};
  const itemSystemChildren = api.tree?.Item5e?.children?.system?.children ?? api.tree?.item?.children?.system?.children ?? {};
  const activityChildren = api.tree?.activity?.children ?? {};
  function buildArrayItemChildren() {
    const indexed = {};
    for (let i = 0; i <= 4; i++) {
      indexed[String(i)] = {
        type: "property",
        detail: "object",
        info: `Item at index ${i} (item.getRollData() format)`,
        children: itemSystemChildren
      };
    }
    return indexed;
  }
  __name(buildArrayItemChildren, "buildArrayItemChildren");
  const actorEntries = {
    "actor.raceOrType": { type: "property", detail: "string", info: "Race (PCs) or creature type (NPCs)" },
    "actor.typeOrRace": { type: "property", detail: "string", info: "Creature type (NPCs) or race (PCs)" },
    "actor.isCombatTurn": { type: "property", detail: "boolean", info: "Whether it's this actor's combat turn" },
    canSee: { type: "property", detail: "boolean", info: "Whether actor can see the target" },
    canSense: { type: "property", detail: "boolean", info: "Whether actor can sense the target" },
    equippedItems: { type: "property", detail: "object[]", info: "Actor's equipped items (item.getRollData() format)", children: buildArrayItemChildren() },
    isAttuned: { type: "property", detail: "boolean", info: "Whether the triggering item is attuned" },
    items: { type: "property", detail: "object[]", info: "Actor's items (item.getRollData() format)", children: buildArrayItemChildren() },
    tokenUuid: { type: "property", detail: "string", info: "Actor's token document UUID" }
  };
  for (const [key, entry] of Object.entries(actorSystemChildren)) {
    const children = entry?.children ?? entry?.instanceChildren;
    if (children && Object.keys(children).length > 0) {
      actorEntries[`actor.${key}`] = { ...entry, children };
    }
  }
  const targetEntries = {
    "target.canSee": { type: "property", detail: "boolean", info: "Whether target can see the attacker" },
    "target.canSense": { type: "property", detail: "boolean", info: "Whether target can sense the attacker" },
    "target.equippedItems": { type: "property", detail: "object[]", info: "Target's equipped items (item.getRollData() format)", children: buildArrayItemChildren() },
    "target.failedSave": { type: "property", detail: "boolean", info: "Whether target failed the save" },
    "target.isCombatTurn": { type: "property", detail: "boolean", info: "Whether it's the target's combat turn" },
    "target.isHit": { type: "property", detail: "boolean", info: "Whether target was hit by the attack" },
    "target.isHitEC": { type: "property", detail: "boolean", info: "Whether target was hit (including EC)" },
    "target.items": { type: "property", detail: "object[]", info: "Target's items (item.getRollData() format)", children: buildArrayItemChildren() },
    "target.raceOrType": { type: "property", detail: "string", info: "Target's race or creature type" },
    "target.saved": { type: "property", detail: "boolean", info: "Whether target passed the save" },
    "target.semiSuperSaver": { type: "property", detail: "boolean", info: "Whether target is a semi-super saver" },
    "target.superSaver": { type: "property", detail: "boolean", info: "Whether target is a super saver" },
    "target.typeOrRace": { type: "property", detail: "string", info: "Target's creature type or race" },
    raceOrType: { type: "property", detail: "string", info: "Target's race or creature type" },
    targetActorUuid: { type: "property", detail: "string", info: "Target actor UUID" },
    targetUuid: { type: "property", detail: "string", info: "Target token document UUID" },
    typeOrRace: { type: "property", detail: "string", info: "Target's creature type or race" }
  };
  for (const [key, entry] of Object.entries(actorSystemChildren)) {
    const children = entry?.children ?? entry?.instanceChildren;
    if (children && Object.keys(children).length > 0) {
      targetEntries[`target.${key}`] = { ...entry, children };
    }
  }
  return {
    "Actor Data": actorEntries,
    "Target Data": targetEntries,
    "Workflow": {
      hasAttack: { type: "property", detail: "boolean", info: "Whether the activity has an attack roll" },
      hasDamage: { type: "property", detail: "boolean", info: "Whether the activity deals damage" },
      hasSave: { type: "property", detail: "boolean", info: "Whether the workflow includes a save" },
      isConcentrationCheck: { type: "property", detail: "boolean", info: "Whether this is a concentration check" },
      isDeathSave: { type: "property", detail: "boolean", info: "Whether this is a death saving throw" },
      item: { type: "property", detail: "object", info: "Triggering item (item.getRollData() format)", children: itemSystemChildren },
      shouldRollDamage: { type: "property", detail: "boolean", info: "Whether damage should be rolled" },
      "workflow.activity": { type: "variable", detail: "Activity", info: "The workflow's triggering activity", children: activityChildren }
    },
    "World": {
      worldTime: { type: "property", detail: "number", info: "Current world time (game.time.worldTime)" }
    },
    "Combat": {
      combatRound: { type: "property", detail: "number", info: "Current combat round number" },
      combatTime: { type: "property", detail: "number", info: "Combat time (round + turn/100)" },
      combatTurn: { type: "property", detail: "number", info: "Current combat turn number" },
      isCombatTurn: { type: "property", detail: "boolean", info: "Whether it's the actor's combat turn" }
    },
    "Functions": {
      ...midiChildren.findNearby ? { findNearby: midiChildren.findNearby } : {},
      ...midiChildren.findNearbyCount ? { findNearbyCount: midiChildren.findNearbyCount } : {},
      ...midiChildren.checkNearby ? { checkNearby: midiChildren.checkNearby } : {},
      ...midiChildren.hasCondition ? { hasCondition: midiChildren.hasCondition } : {},
      ...midiChildren.checkIncapacitated ? { checkIncapacitated: midiChildren.checkIncapacitated } : {},
      ...midiChildren.computeDistance ? { computeDistance: midiChildren.computeDistance } : {},
      ...midiChildren.sizeNumeric ? { sizeNumeric: midiChildren.sizeNumeric } : {},
      checkDefeated: { type: "function", detail: "(actorRef) => 0 | 1", info: "Check if actor/token is defeated" },
      fromUuidSync: { type: "function", detail: "(uuid) => Document | null", info: "Synchronously resolve a UUID to a Document" },
      evalRaceOrType: { type: "function", detail: "(actor) => string", info: "Get race (PCs) or creature type (NPCs) of an actor" },
      evalTypeOrRace: { type: "function", detail: "(actor) => string", info: "Get creature type (NPCs) or race (PCs) of an actor" }
    }
  };
}
__name(buildBlocklyCategories, "buildBlocklyCategories");
var ConditionEditor = class _ConditionEditor extends HandlebarsApplicationMixin8(ApplicationV28) {
  static {
    __name(this, "ConditionEditor");
  }
  constructor(editorOptions, appOptions) {
    super(appOptions);
    this.originalValue = editorOptions.value;
    this.flagKey = editorOptions.key;
    this.effect = editorOptions.effect;
    this.label = editorOptions.label;
    this.parentDoc = editorOptions.parent;
    this.onSubmitCallback = editorOptions.onSubmit;
    this.onCancelCallback = editorOptions.onCancel;
    this.submitted = false;
  }
  static DEFAULT_OPTIONS = {
    id: "midi-qol-condition-editor",
    window: {
      title: "midi-qol.ConditionEditor.Title",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      width: 650,
      height: 400
    },
    actions: {
      save: _ConditionEditor.#onSave,
      cancel: _ConditionEditor.#onCancel
    }
  };
  static PARTS = {
    form: {
      template: "modules/midi-qol/templates/apps/ConditionEditor/conditionEditor.hbs"
    }
  };
  get title() {
    const name = this.effect?.name ?? this.label ?? "";
    return `${i18n("midi-qol.ConditionEditor.Title")}: ${name}`;
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.value = this.originalValue;
    context.key = this.flagKey;
    return context;
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    const codeMirrorEl = this.element.querySelector("code-mirror");
    if (!codeMirrorEl) return;
    const editorValue = this.originalValue || "true && true";
    codeMirrorEl.value = editorValue;
    const macroAutoComplete = game.modules?.get("macro-autocomplete");
    const api = macroAutoComplete?.api;
    if (!api?.setContextCompletions) return;
    const completions = buildConditionCompletions(api);
    const blocklyCategories = buildBlocklyCategories(api);
    api.setContextCompletions(codeMirrorEl, completions, blocklyCategories);
  }
  /** Read current value from the code-mirror element */
  #getCurrentValue() {
    const codeMirrorEl = this.element.querySelector("code-mirror");
    if (!codeMirrorEl) return this.originalValue;
    return codeMirrorEl.value ?? this.originalValue;
  }
  get hasUnsavedChanges() {
    return this.#getCurrentValue() !== this.originalValue;
  }
  async close(options) {
    if (!this.submitted && this.hasUnsavedChanges) {
      const confirmed = await foundry.applications.api.DialogV2.confirm({
        window: { title: i18n("midi-qol.OverTimeEditor.UnsavedTitle") },
        content: `<p>${i18n("midi-qol.OverTimeEditor.UnsavedContent")}</p>`,
        yes: { label: i18n("midi-qol.OverTimeEditor.CloseWithoutSaving") },
        no: { label: i18n("midi-qol.OverTimeEditor.Cancel") }
      });
      if (!confirmed) return this;
    }
    const api = game.modules?.get("macro-autocomplete")?.api;
    const codeMirrorEl = this.element?.querySelector("code-mirror");
    if (api?.clearContextCompletions && codeMirrorEl) {
      api.clearContextCompletions(codeMirrorEl);
    }
    return super.close(options);
  }
  static #onSave() {
    const value = this.#getCurrentValue();
    this.submitted = true;
    this.onSubmitCallback(value);
    this.close();
  }
  static #onCancel() {
    this.submitted = true;
    this.onCancelCallback();
    this.close();
  }
  async _preClose(options) {
    await super._preClose(options);
    if (!this.submitted) {
      this.onCancelCallback();
    }
  }
};

// src/module/undo.ts
var undoDataQueue = [];
var startedUndoDataQueue = [];
var MAXUNDO = 15;
function _queueUndoDataDirect(undoDataDef) {
  if (!configSettings.undoWorkflow) return;
  const tokenDoc = fromUuidSync(undoDataDef.tokendocUuid);
  const actor = fromUuidSync(undoDataDef.actorUuid);
  if (!actor) return;
  const undoData = {
    id: undoDataDef.id ?? foundry.utils.randomID(),
    actorUuid: undoDataDef.actorUuid,
    actorEntry: { actorUuid: undoDataDef.actorUuid, tokenUuid: undoDataDef.tokendocUuid, actorData: actor?.toObject(true), tokenData: tokenDoc?.toObject(true) },
    chatCardUuids: undoDataDef.chatCardUuids ?? [],
    itemCardUuid: undoDataDef.itemCardUuid,
    actorName: actor.name,
    itemName: undoDataDef.itemName,
    userName: undoDataDef.userName,
    allTargets: new Collection(),
    serverTime: game.time?.serverTime,
    templateUuids: undoDataDef.templateUuids ?? [],
    isReaction: undoDataDef.isReaction
  };
  for (let undoEntry of undoDataDef.targets ?? []) {
    let { actorUuid, tokenUuid } = undoEntry;
    const targetData = createTargetData(tokenUuid);
    if (targetData) {
      undoData.allTargets?.set(actorUuid, targetData);
    }
  }
  addQueueEntry(undoDataQueue, undoData);
}
__name(_queueUndoDataDirect, "_queueUndoDataDirect");
async function saveUndoData(workflow) {
  if (!configSettings.undoWorkflow) return true;
  workflow.undoData = {
    id: workflow.id,
    userId: game.user?.id,
    itemName: workflow.item.name,
    itemUuid: workflow.item.uuid,
    userName: game.user?.name,
    tokendocUuid: workflow.token?.document.uuid,
    actorUuid: workflow.actor.uuid,
    actorName: workflow.actor.name,
    chatCardUuids: [],
    isReaction: workflow.workflowOptions?.isReaction || isReactionItem(workflow.item),
    templateUuids: [],
    sequencerUuid: workflow.item.uuid
  };
  if (!await unTimedExecuteAsGM("startUndoWorkflow", workflow.undoData)) {
    error2("Could not startUndoWorkflow");
    return false;
  }
  return true;
}
__name(saveUndoData, "saveUndoData");
function createTargetData(tokenUuid) {
  if (!tokenUuid) return void 0;
  const tokenDoc = fromUuidSync(tokenUuid);
  if (!tokenDoc) {
    error2("undo | createTargetData could not fetch token document for ", tokenUuid);
    return void 0;
  }
  const targetData = { tokenUuid, actorUuid: tokenDoc.actor?.uuid ?? "", actorData: tokenDoc.actor?.toObject(true), tokenData: tokenDoc.toObject(true) };
  return targetData;
}
__name(createTargetData, "createTargetData");
function startUndoWorkflow(undoData) {
  if (!configSettings.undoWorkflow) return true;
  let actor = fromUuidSync(undoData.actorUuid);
  if (actor instanceof TokenDocument) actor = actor.actor;
  const actorData = actor?.toObject(true);
  const tokenData = actor?.isToken ? actor.token.toObject(true) : fromUuidSync(undoData.tokendocUuid ?? "")?.toObject(true);
  undoData.actorEntry = { actorUuid: undoData.actorUuid, tokenUuid: undoData.tokendocUuid, actorData, tokenData };
  undoData.allTargets = new Collection();
  const concentrationEffect = getConcentrationEffect(actor, undoData.itemUuid);
  if (concentrationEffect) {
    for (let dependent of concentrationEffect.getDependents()) {
      let token;
      if (dependent instanceof ActiveEffect) dependent = dependent.parent;
      if (dependent instanceof Actor && dependent.isToken) token = dependent.token;
      else if (dependent instanceof Actor) token = dependent.getActiveTokens(false, true)[0];
      if (!token) continue;
      const targetData = createTargetData(token.uuid);
      if (targetData) undoData.allTargets.set(dependent.uuid, targetData);
    }
  }
  addQueueEntry(startedUndoDataQueue, undoData);
  return true;
}
__name(startUndoWorkflow, "startUndoWorkflow");
function updateUndoChatCardUuidsById(data) {
  if (!configSettings.undoWorkflow) return;
  const currentUndo = undoDataQueue.find((undoEntry) => undoEntry.id === data.id);
  if (!currentUndo) {
    console.warn("midi-qol | updateUndoChatCardUuidsById | Could not find existing entry for ", data);
    return;
  }
  currentUndo.chatCardUuids = data.chatCardUuids;
}
__name(updateUndoChatCardUuidsById, "updateUndoChatCardUuidsById");
function updateUndoChatCardUuids(data) {
  if (!configSettings.undoWorkflow) return;
  const currentUndo = undoDataQueue.find((undoEntry) => undoEntry.serverTime === data.serverTime && undoEntry.userId === data.userId);
  if (!currentUndo) {
    console.warn("midi-qol | updateUndoChatCardUuids | Could not find existing entry for ", data);
    return;
  }
  currentUndo.chatCardUuids = data.chatCardUuids;
}
__name(updateUndoChatCardUuids, "updateUndoChatCardUuids");
async function saveTargetsUndoData(workflow) {
  if (!workflow.undoData) return;
  workflow.undoData.targets = [];
  workflow.targets.forEach((t) => {
    let tokenDoc = getTokenDocument(t);
    if (!tokenDoc) return;
    if (tokenDoc?.actor?.uuid === workflow.actor.uuid) return;
    workflow.undoData.targets.push({ tokenUuid: tokenDoc?.uuid, actorUuid: tokenDoc?.actor?.uuid ?? "" });
  });
  workflow.undoData.serverTime = game.time?.serverTime;
  workflow.undoData.itemCardUuid = workflow.itemCardUuid;
  for (const tUuid of workflow.templateUuids) {
    if (tUuid) workflow.undoData.templateUuids.push(tUuid);
  }
  return unTimedExecuteAsGM("queueUndoData", workflow.undoData);
}
__name(saveTargetsUndoData, "saveTargetsUndoData");
async function addUndoChatMessage(message) {
  const currentUndo = undoDataQueue[0];
  if (message instanceof Promise) message = await message;
  if (configSettings.undoWorkflow && currentUndo && !currentUndo.chatCardUuids.some((uuid) => uuid === message.uuid)) {
    currentUndo.chatCardUuids = currentUndo.chatCardUuids.concat([message.uuid]);
    unTimedExecuteAsGM("updateUndoChatCardUuids", currentUndo);
  }
}
__name(addUndoChatMessage, "addUndoChatMessage");
Hooks.on("createChatMessage", (message, data, user) => {
  if (!configSettings.undoWorkflow) return;
  if ((undoDataQueue ?? []).length < 1) return;
  const currentUndo = undoDataQueue[0];
  const speaker = message.speaker;
  if (speaker.token) {
    const tokenUuid = `Scene.${speaker.scene}.Token.${speaker.token}`;
    const actorUuid = fromUuidSync(tokenUuid)?.actor?.uuid;
    if (actorUuid && currentUndo.allTargets?.has(actorUuid)) currentUndo.chatCardUuids.push(message.uuid);
  } else if (speaker.actor) {
    const actorUuid = `Actor.${speaker.actor}`;
    if (currentUndo.allTargets?.has(actorUuid)) currentUndo.chatCardUuids.push(message.uuid);
  }
});
function showUndoQueue() {
  console.log(undoDataQueue);
  log("Undo queue size is ", new TextEncoder().encode(JSON.stringify(undoDataQueue)).length);
  log("Started queue size is ", new TextEncoder().encode(JSON.stringify(startedUndoDataQueue)).length);
}
__name(showUndoQueue, "showUndoQueue");
function getUndoQueue() {
  return undoDataQueue;
}
__name(getUndoQueue, "getUndoQueue");
function queueUndoData(data) {
  let inProgress = startedUndoDataQueue.find((undoData) => undoData.userId === data.userId && undoData.id === data.id);
  if (!inProgress) {
    error2("Could not find started undo entry for ", data.userId, data.id);
    return false;
  }
  ;
  inProgress = foundry.utils.mergeObject(inProgress, data, { overwrite: false });
  startedUndoDataQueue = startedUndoDataQueue.filter((undoData) => undoData.userId !== data.userId || undoData.itemUuid !== data.itemUuid);
  data.targets?.forEach((undoEntry) => {
    if (!inProgress.allTargets?.get(undoEntry.actorUuid ?? "")) {
      const targetData = createTargetData(undoEntry.tokenUuid);
      if (targetData) {
        foundry.utils.mergeObject(undoEntry, targetData, { inplace: true });
        inProgress.allTargets?.set(undoEntry.actorUuid, undoEntry);
      }
    }
  });
  addQueueEntry(undoDataQueue, inProgress);
  return true;
}
__name(queueUndoData, "queueUndoData");
function addQueueEntry(queue, data) {
  let added = false;
  for (let i = 0; i < queue.length; i++) {
    if ((data.serverTime ?? 0) > (queue[i].serverTime ?? 0)) {
      queue.splice(i, 0, data);
      added = true;
      break;
    }
  }
  if (!added) queue.push(data);
  Hooks.callAll("midi-qol.addUndoEntry", data);
  if (queue.length > MAXUNDO) {
    log("Removed undoEntry due to overflow", queue.pop());
  }
}
__name(addQueueEntry, "addQueueEntry");
async function undoMostRecentWorkflow() {
  return unTimedExecuteAsGM("undoMostRecentWorkflow");
}
__name(undoMostRecentWorkflow, "undoMostRecentWorkflow");
async function removeMostRecentWorkflow() {
  return unTimedExecuteAsGM("removeMostRecentWorkflow");
}
__name(removeMostRecentWorkflow, "removeMostRecentWorkflow");
async function undoTillWorkflow(workflowId, undoTarget, removeWorkflow = false) {
  if (undoDataQueue.length === 0) return false;
  if (!undoDataQueue.find((ue) => ue.id === workflowId)) return false;
  const queueLength = undoDataQueue.length;
  try {
    while (undoDataQueue.length > 0 && undoDataQueue[0].id !== workflowId) {
      await undoWorkflow(undoDataQueue.shift());
    }
    if (undoTarget) await undoWorkflow(undoDataQueue[0]);
    if (undoDataQueue.length > 0 && removeWorkflow) {
      const undoData = undoDataQueue.shift();
      if (undoData) {
        socketlibSocket.executeAsUser("removeWorkflow", undoData.userId, undoData.id);
      }
    }
  } finally {
    if (queueLength !== undoDataQueue.length) Hooks.callAll("midi-qol.removeUndoEntry");
  }
  return queueLength !== undoDataQueue.length;
}
__name(undoTillWorkflow, "undoTillWorkflow");
async function _undoMostRecentWorkflow() {
  if (undoDataQueue.length === 0) return false;
  let undoData;
  try {
    while (undoDataQueue.length > 0) {
      undoData = undoDataQueue.shift();
      if (undoData?.isReaction) await undoWorkflow(undoData);
      else return undoWorkflow(undoData);
    }
  } finally {
    if (undoData) Hooks.callAll("midi-qol.removeUndoEntry", undoData);
  }
  return;
}
__name(_undoMostRecentWorkflow, "_undoMostRecentWorkflow");
async function _removeMostRecentWorkflow() {
  if (undoDataQueue.length === 0) return false;
  let undoData;
  try {
    while (undoDataQueue.length > 0) {
      undoData = undoDataQueue.shift();
      if (undoData?.isReaction) continue;
      else return undoData;
    }
  } finally {
    if (undoData) Hooks.callAll("midi-qol.removeUndoEntry", undoData);
  }
  return;
}
__name(_removeMostRecentWorkflow, "_removeMostRecentWorkflow");
async function _removeChatCards(data) {
  if (!data.chatCardUuids) return;
  try {
    for (let uuid of data.chatCardUuids) {
      const card = fromUuidSync(uuid);
      await removeChatCard(card);
    }
  } catch (err) {
    error2(err);
  }
}
__name(_removeChatCards, "_removeChatCards");
function getRemoveUndoEffects(effectsData, actor) {
  if (!effectsData) return [];
  const effectsToRemove = actor.effects.filter((effect) => {
    return !effectsData.some((effectData) => effect.id === effectData._id);
  }).map((effect) => effect.id) ?? [];
  return effectsToRemove;
}
__name(getRemoveUndoEffects, "getRemoveUndoEffects");
function getRemoveUndoItems(itemsData, actor) {
  if (!itemsData) return [];
  const itemsToRemove = actor.items.filter((item) => {
    return !itemsData?.some((itemData) => item.id === itemData._id);
  }).map((item) => item.id);
  return itemsToRemove;
}
__name(getRemoveUndoItems, "getRemoveUndoItems");
function getChanges(newData, savedData) {
  if (!newData && !savedData) return {};
  if (newData) {
    delete newData.items;
    delete newData.effects;
  }
  if (savedData) {
    delete savedData.items;
    delete savedData.effects;
  }
  const changes = foundry.utils.flattenObject(foundry.utils.diffObject(newData ?? {}, savedData ?? {}));
  const tempChanges = foundry.utils.flattenObject(foundry.utils.diffObject(savedData ?? {}, newData ?? {}));
  const toDelete = {};
  for (let key of Object.keys(tempChanges)) {
    if (!changes[key]) {
      let parts = key.split(".");
      parts[parts.length - 1] = "-=" + parts[parts.length - 1];
      let newKey = parts.join(".");
      toDelete[newKey] = null;
    }
  }
  return foundry.utils.mergeObject(changes, toDelete);
}
__name(getChanges, "getChanges");
async function undoSingleTokenActor({ tokenUuid, actorUuid, actorData, tokenData }) {
  let actor = fromUuidSync(actorUuid ?? "");
  if (actor instanceof TokenDocument) actor = actor.actor;
  const tokenDoc = actor?.isToken ? actor.token : fromUuidSync(tokenUuid ?? "");
  if (!actor) return;
  let actorChanges;
  let tokenChanges;
  if (debugEnabled > 0) warn("undoSingleTokenActor | starting for ", actor.name);
  const removeItemsFunc = /* @__PURE__ */ __name(async () => {
    const itemsToRemove = getRemoveUndoItems(actorData?.items ?? [], actor);
    if (itemsToRemove?.length > 0) await actor.deleteEmbeddedDocuments("Item", itemsToRemove, { isUndo: true });
    if (debugEnabled > 0) warn("undoSingleTokenActor | items to remove ", actor.name, itemsToRemove);
  }, "removeItemsFunc");
  if (globalThis.DAE.actionQueue) await globalThis.DAE.actionQueue.add(removeItemsFunc);
  else await removeItemsFunc();
  if (debugEnabled > 0) warn("undoSingleTokenActor |  removeItemFunc completed");
  if (debugEnabled > 0) warn("undoSingleTokenActor | about to remove effects");
  const removeEffectsFunc = /* @__PURE__ */ __name(async () => {
    const effectsToRemove = getRemoveUndoEffects(actorData?.effects ?? [], actor);
    if (debugEnabled > 0) warn("undoSingleTokenActor |", effectsToRemove);
    if (effectsToRemove.length > 0) await actor.deleteEmbeddedDocuments("ActiveEffect", effectsToRemove, { noConcentrationCheck: true, isUndo: true });
  }, "removeEffectsFunc");
  if (globalThis.DAE?.actionQueue) await globalThis.DAE.actionQueue.add(removeEffectsFunc);
  else await removeEffectsFunc();
  if (debugEnabled > 0) warn("undoSingleTokenActor | remove effects completed");
  const itemsToAdd = actorData?.items?.filter((itemData) => (
    /*!itemData.flags?.dae?.DAECreated && */
    !actor.items.some((item) => itemData._id === item.id)
  ));
  if (debugEnabled > 0) warn("undoSingleTokenActor | Items to add ", actor.name, itemsToAdd);
  if ((itemsToAdd?.length ?? 0) > 0) {
    if (globalThis.DAE?.actionQueue) await globalThis.DAE.actionQueue.add(actor.createEmbeddedDocuments.bind(actor), "Item", itemsToAdd, { keepId: true, isUndo: true });
    else await actor?.createEmbeddedDocuments("Item", itemsToAdd, { keepId: true, isUndo: true });
    await busyWait(100);
  }
  let effectsToAdd = actorData?.effects?.filter((efData) => !actor.effects.some((effect) => efData._id === effect.id));
  effectsToAdd = effectsToAdd?.filter((efData) => !efData?.flags?.dae?.transfer) ?? [];
  if (debugEnabled > 0) warn("undoSingleTokenActor | Effects to add ", actor.name, effectsToAdd);
  if (effectsToAdd?.length > 0) {
    if (globalThis.DAE?.actionQueue) await globalThis.DAE.actionQueue.add(async () => {
      effectsToAdd = effectsToAdd?.filter((eff) => !actor.effects.some((effect) => effect.id === eff._id));
      if (debugEnabled > 0) warn("undoSingleTokenActor | Effects to add are ", effectsToAdd, actor.name);
      await actor.createEmbeddedDocuments("ActiveEffect", effectsToAdd, { keepId: true, isUndo: true });
    });
    else await actor.createEmbeddedDocuments("ActiveEffect", effectsToAdd, { keepId: true, isUndo: true });
  }
  if (actorData && globalThis.DAE?.actionQueue) await globalThis.DAE.actionQueue.add(actor.updateEmbeddedDocuments.bind(actor), "Item", actorData.items, { keepId: true, isUndo: true });
  else if (actorData) await actor.updateEmbeddedDocuments("Item", actorData.items, { keepId: true, isUndo: true });
  if ((actorData?.effects?.length ?? 0) > 0) {
    if (globalThis.DAE?.actionQueue) await globalThis.DAE.actionQueue.add(actor.updateEmbeddedDocuments.bind(actor), "ActiveEffect", actorData.effects, { keepId: true, isUndo: true });
    else await actor.updateEmbeddedDocuments("ActiveEffect", actorData.effects, { keepId: true, isUndo: true });
  }
  actorChanges = actorData ? getChanges(actor.toObject(true), actorData) : {};
  if (debugEnabled > 0) warn("undoSingleTokenActor | Actor data ", actor.name, actorData, actorChanges);
  if (!foundry.utils.isEmpty(actorChanges)) {
    delete actorChanges.items;
    delete actorChanges.effects;
    await actor.update(actorChanges, { noConcentrationCheck: true });
  }
  if (tokenDoc) {
    tokenChanges = tokenData ? getChanges(tokenDoc.toObject(true), tokenData) : {};
    delete tokenChanges.actorData;
    delete tokenChanges.delta;
    if (!foundry.utils.isEmpty(tokenChanges)) {
      await tokenDoc.update(tokenChanges, { noConcentrationCheck: true });
    }
  }
}
__name(undoSingleTokenActor, "undoSingleTokenActor");
async function removeChatCard(chatCard) {
  if (!chatCard || !chatCard.content) return;
  const shouldDelete = configSettings.undoChatColor === "Delete";
  if (shouldDelete) {
    if (debugEnabled > 1) debug("Deleting chat card ", chatCard.id, chatCard.uuid);
    return await chatCard.delete();
  }
  return await chatCard.update({ content: `<div style="background-color: ${configSettings.undoChatColor};"> ${chatCard.content}</div>` });
}
__name(removeChatCard, "removeChatCard");
async function undoWorkflow(undoData) {
  if (!undoData) return;
  log(`Undoing workflow for Player ${undoData.userName} Token: ${undoData.actorEntry?.actorData?.name} Item: ${undoData.itemName ?? ""}`);
  for (let templateUuid of undoData.templateUuids)
    await fromUuidSync(templateUuid)?.delete();
  if (globalThis.Sequencer && undoData.sequencerUuid) await globalThis.Sequencer.EffectManager.endEffects({ origin: undoData.sequencerUuid });
  for (let undoEntry of undoData?.allTargets ?? []) {
    log("undoing target ", undoEntry.actorData?.name ?? undoEntry.tokenData?.name, undoEntry);
    await undoSingleTokenActor(undoEntry);
  }
  ;
  if (undoData.actorEntry) await undoSingleTokenActor(undoData.actorEntry);
  const shouldDelete = false;
  if (undoData.itemCardUuid) {
    const message = fromUuidSync(undoData.itemCardUuid);
    await removeChatCard(message);
  }
  await _removeChatCards({ chatCardUuids: undoData.chatCardUuids });
}
__name(undoWorkflow, "undoWorkflow");

// src/module/apps/TargetConfirmation.ts
var ApplicationV29 = foundry.applications.api.ApplicationV2;
var { HandlebarsApplicationMixin: HandlebarsApplicationMixin9 } = foundry.applications.api;
var TargetConfirmationDialog = class _TargetConfirmationDialog extends HandlebarsApplicationMixin9(ApplicationV29) {
  static {
    __name(this, "TargetConfirmationDialog");
  }
  callback;
  data;
  hookId;
  constructor(actor, activity, user, options = {}) {
    super(options);
    this.position.left = _TargetConfirmationDialog.xPosition;
    this.position.top = _TargetConfirmationDialog.yPosition;
    let maxTargets = activity.target?.affects?.count ? Number(activity.target.affects.count) : Infinity;
    const inCombat = activity.item.parent && isInCombat(activity.item.parent);
    const requiresTargets = configSettings.requiresTargets === "always" || configSettings.requiresTargets === "tokens" && (canvas?.scene?.tokens.size ?? 0) > 0 || configSettings.requiresTargets === "combat" && inCombat;
    if (activity.item.type === "weapon" && requiresTargets && shouldEnforceSingleTarget(activity)) {
      maxTargets = 1;
    }
    this.data = { actor, activity, user, targets: [], options, maxTargets };
    const keys = {};
    this.callback = function(value) {
      foundry.utils.setProperty(options, "workflowOptions.advantage", options.workflowOptions?.advantage);
      foundry.utils.setProperty(options, "workflowOptions.disadvantage", options.workflowOptions?.disadvantage);
      foundry.utils.setProperty(options, "workflowOptions.fastForward", options.workflowOptions?.fastForward);
      return options.callback ? options.callback(value) : value;
    };
    this.validateAndUpdateTargets();
    if (["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(checkRule("checkFlanking")) && game.user?.targets) {
      const actor2 = this.data.activity.actor;
      const token = tokenForActor(actor2);
      if (token)
        for (let target of game.user?.targets)
          markFlanking(token, target);
    }
    this.hookId = Hooks.on("targetToken", (user2, token, targeted) => {
      if (user2 !== game.user) return;
      if (game.user.targets) {
        this.validateAndUpdateTargets();
      }
      if (this.data.maxTargets && game.user.targets?.size > this.data.maxTargets)
        ui.notifications?.warn(i18nFormat2("midi-qol.wrongNumberTargets", { allowedTargets: this.data.maxTargets }));
      const newTargets = Array.from(game.user.targets ?? []);
      if (!newTargets.equals(this.data.targets ?? [])) {
        this.data.targets = newTargets;
        this.render();
      }
    });
    return this;
  }
  get title() {
    return this.data.options.title ?? i18n("midi-qol.TargetConfirmation.Name");
  }
  static get xPosition() {
    const left = 100;
    const middle = window.innerWidth / 2 - 155;
    let adjustment = 20;
    const right = window.innerWidth - adjustment - (ui.sidebar?.element.clientWidth ?? 10) - 300;
    switch (targetConfirmation.gridPosition?.x) {
      case -1:
        return left;
      case 0:
        return middle;
      default:
      case 1:
        return right;
    }
  }
  static get yPosition() {
    const top = 100;
    const middle = window.innerHeight / 2 - 100;
    const bottom = window.innerHeight - 200;
    switch (targetConfirmation.gridPosition?.y) {
      case -1:
        return top;
      case 0:
        return middle;
      default:
      case 1:
        return bottom;
    }
  }
  static PARTS = {
    main: { template: "modules/midi-qol/templates/apps/TargetConfirmation/targetConfirmation.hbs" },
    footer: { template: "templates/generic/form-footer.hbs" }
  };
  static DEFAULT_OPTIONS = {
    id: "midi-qol-targetConfirmation",
    window: {
      title: "midi-qol.TargetConfirmation.Name",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    classes: ["midi-targeting"],
    position: {
      width: 300,
      height: "auto",
      left: 100,
      top: 100
    },
    actions: {
      roll: this.#onRoll,
      cancel: this.#onCancel,
      clearTargets: this.#onClearTargets
    }
  };
  validateAndUpdateTargets() {
    const targets = Array.from(game.user?.targets ?? []);
    const actor = this.data.activity.actor;
    const attackerToken = tokenForActor(actor);
    let validTargets = [];
    for (let target of targets) {
      if (!isValidTarget(target)) continue;
      if (this.data.options?.workflowOptions?.excludeTargets && this.data.options.workflowOptions.excludeTargets?.includes(target.document.uuid)) continue;
      switch (this.data.activity.target?.affects?.type ?? "any") {
        case "enemy":
          if ((attackerToken?.document?.disposition ?? 1) * target.document.disposition >= 0) continue;
          break;
        case "ally":
          if ((attackerToken?.document?.disposition ?? 1) * target.document.disposition < 0) continue;
          break;
        default:
          break;
      }
      validTargets.push(target.id);
    }
    updateUserTargets(validTargets);
  }
  async _prepareContext(options) {
    let context = { ...this.data, ...await super._prepareContext(options) };
    const targets = Array.from(game.user?.targets ?? []);
    context.targets = [];
    const actor = this.data.activity.actor;
    const token = tokenForActor(actor);
    for (let target of targets) {
      let img = target.document.texture.src;
      if (foundry.helpers.media.VideoHelper.hasVideoExtension(img)) {
        img = await game.video?.createThumbnail(img, { width: 50, height: 50 }) ?? "";
      }
      let details = [];
      if (["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(checkRule("checkFlanking"))) {
        if (token && await computeFlankingStatus(token, target)) details.push(i18n("midi-qol.Flanked") ?? "Flanked");
      }
      let attackerToken = token;
      if (token && checkMechanic("checkRange") !== "none" && ["mwak", "msak", "rwak", "rsak", "utility", "check", "save", "damage"].includes(this.data.activity.actionType)) {
        const { result, attackingToken } = checkActivityRange(this.data.activity, token, /* @__PURE__ */ new Set([target]), false);
        if (attackingToken) attackerToken = attackingToken;
        switch (result) {
          case "normal":
            details.push(`${i18n("DND5E.RangeNormal")}`);
            break;
          case "dis":
            details.push(`${i18n("DND5E.RangeLong")}`);
            break;
          case "fail":
            details.push(`${i18n("midi-qol.OutOfRange")}`);
            break;
        }
      }
      if (!activityHasAreaTarget(this.data.activity)) {
        const hasCoverModule = typeof configSettings.optionalRules.coverCalculation === "string" && configSettings.optionalRules.coverCalculation !== "none";
        const targetCover = attackerToken ? getCoverBonus(attackerToken, target, this.data.activity) : 0;
        if (targetCover > 0 || hasCoverModule) {
          switch (targetCover) {
            case HALF_COVER:
              details.push(`${i18n("DND5E.CoverHalf")} ${i18n("DND5E.Cover")}`);
              break;
            case THREE_QUARTERS_COVER:
              details.push(`${i18n("DND5E.CoverThreeQuarters")} ${i18n("DND5E.Cover")}`);
              break;
            case TOTAL_COVER:
              details.push(`${i18n("DND5E.CoverTotal")} ${i18n("DND5E.Cover")}`);
              break;
            case 0:
              details.push(`${i18n("No")} ${i18n("DND5E.Cover")}`);
              break;
            default:
              details.push(`+${targetCover} ${i18n("DND5E.Cover")}`);
              break;
          }
        }
      }
      let name;
      if (game.user?.isGM) {
        name = getTokenName(target);
      } else {
        name = getTokenPlayerName(target) ?? "";
      }
      const relativeDisposition = (token?.document.disposition ?? 0) * target.document.disposition;
      let displayedDisposition = void 0;
      if (target.document.disposition !== CONST.TOKEN_DISPOSITIONS.SECRET) {
        if (relativeDisposition === CONST.TOKEN_DISPOSITIONS.FRIENDLY) {
          displayedDisposition = i18n("TOKEN.DISPOSITION.FRIENDLY");
        }
      }
      context.targets.push({
        name,
        // : name: game.user.isGM ? getLinkText(target.actor) : getTokenPlayerName(target),
        img,
        displayedDisposition,
        details: details.join(" - "),
        hasDetails: details.length > 0,
        uuid: target.document.uuid
      });
    }
    if (this.data.activity.target) {
      if (this.data.maxTargets)
        context.blurb = i18nFormat2("midi-qol.TargetConfirmation.Blurb", { targetCount: this.data.maxTargets, targetType: this.data.activity.target.affects.type || "targets" });
      else context.blurb = i18n("midi-qol.TargetConfirmation.BlurbAny");
    }
    context.buttons = [
      { type: "button", icon: "fa-solid fa-dice-d20", label: "DND5E.Roll", action: "roll" },
      { type: "button", icon: "fa-solid fa-stop", label: "Cancel", action: "cancel" },
      { type: "hidden", icon: "fa-solid fa-check", label: "Clear", action: "clearTargets" }
    ];
    return context;
  }
  static #onRoll() {
    if (this.data.maxTargets && (game.user?.targets?.size ?? 0) > this.data.maxTargets) {
      ui.notifications?.warn(i18nFormat2("midi-qol.wrongNumberTargets", { allowedTargets: this.data.maxTargets }));
      return;
    }
    this.doCallback(true);
    this.close();
  }
  static #onCancel() {
    this.doCallback(false);
    this.close();
  }
  static #onClearTargets() {
    updateUserTargets([]);
    this.data.targets = [];
    this.render();
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    if (canvas) {
      let targetNames = this.element.getElementsByClassName("content-link midi-qol");
      for (let targetName of Array.from(targetNames)) {
        targetName.addEventListener("click", async (event) => {
          event.stopPropagation();
          const doc = await fromUuid(event.currentTarget?.dataset.uuid);
          return doc?.sheet?.render({ force: true });
        });
      }
      let imgs = this.element.getElementsByTagName("img");
      for (let i of Array.from(imgs)) {
        i.style.border = "none";
        i.closest(".midi-qol-box")?.addEventListener("contextmenu", (event) => {
          const token = getToken(i.id);
          if (token) {
            token.setTarget(false, { user: game.user, releaseOthers: false, groupSelection: true });
            this.render();
          }
        });
        i.closest(".midi-qol-box")?.addEventListener("click", async function() {
          const token = getToken(i.id);
          if (token) await canvas.ping(token.center);
        });
        i.closest(".midi-qol-box")?.addEventListener("mouseover", function() {
          const token = getToken(i.id);
          if (token) {
            token.hover = true;
            token.refresh();
          }
        });
        i.closest(".midi-qol-box")?.addEventListener("mouseout", function() {
          const token = getToken(i.id);
          if (token) {
            token.hover = false;
            token.refresh();
          }
        });
      }
    }
  }
  close(options = {}) {
    Hooks.off("targetToken", this.hookId);
    this.doCallback(false);
    return super.close(options);
  }
  #callbackFired = false;
  doCallback(value = false) {
    if (this.#callbackFired) return;
    this.#callbackFired = true;
    try {
      if (this.callback) this.callback(value);
    } catch (err) {
      const message = `TargetConfirmation | calling callback failed`;
      TroubleShooter.recordError(err, message);
      error2(message, err);
    }
  }
};

// src/module/activities/activityHelpers.ts
var { DialogV2: DialogV24 } = foundry.applications.api;
var creatureTargetTypes = ["creature", "enemy", "ally", "willing"];
function shouldEnforceSingleTarget(activity) {
  if (!configSettings.enforceSingleWeaponTarget) return false;
  if (!allAttackTypes.includes(activity.actionType)) return false;
  const isEmanationTargeting = activityHasAutoPlaceTemplate(activity) || activityHasEmanationNoTemplate(activity);
  const isAoETargeting = !isEmanationTargeting && activityHasAreaTarget(activity);
  if (isEmanationTargeting || isAoETargeting) return false;
  const hasExplicitCount = activity.target?.affects?.count && activity.target.affects.count !== "";
  if (hasExplicitCount) return false;
  const affectsType = activity.target?.affects?.type;
  return !affectsType || creatureTargetTypes.includes(affectsType);
}
__name(shouldEnforceSingleTarget, "shouldEnforceSingleTarget");
async function removeFlanking(actor) {
  let CEFlanking = getFlankingEffect();
  if (CEFlanking && CEFlanking.name) await CERemoveEffect({ effectName: CEFlanking.name, uuid: actor.uuid });
}
__name(removeFlanking, "removeFlanking");
function setDamageRollMinTerms(rolls) {
  if (rolls && sumRolls(rolls)) {
    for (let roll of rolls) {
      for (let term of roll.terms) {
        if (term instanceof foundry.dice.terms.Die && term.modifiers.includes(`min${term.faces}`)) {
          for (let result of term.results) {
            result.result = term.faces ?? 1;
          }
        }
      }
    }
  }
}
__name(setDamageRollMinTerms, "setDamageRollMinTerms");
async function doActivityReactions(activity, workflow) {
  const promises = [];
  if (!foundry.utils.getProperty(activity, `flags.${MODULE_ID}.noProvokeReaction`) && !workflow?.workflowOptions.noProvokeReaction) {
    for (let targetToken of workflow.targets) {
      promises.push((async () => {
        const result = await doReactions(targetToken, workflow.tokenUuid, null, "reactionpreattack", { item: activity.item, workflow, workflowOptions: foundry.utils.mergeObject(workflow.workflowOptions, { sourceActorUuid: activity.actor?.uuid, sourceItemUuid: activity.item?.uuid }, { inplace: false, overwrite: true }) });
        if (result.name) {
          targetToken.actor?.reset();
          workflow.actor.reset();
        }
        return result;
      })());
    }
  }
  return await Promise.all(promises);
}
__name(doActivityReactions, "doActivityReactions");
function preActivityConsumptionHook(activity, usageConfig, messageConfig) {
  return true;
}
__name(preActivityConsumptionHook, "preActivityConsumptionHook");
function activityConsumptionHook(activity, usageConfig, messageConfig, updates) {
  return true;
}
__name(activityConsumptionHook, "activityConsumptionHook");
function activityHasScaledTargetCount(activity) {
  const sourceCount = activity.target?.override ? activity._source?.target?.affects?.count : activity.item?.system?._source?.target?.affects?.count;
  if (typeof sourceCount !== "string" || sourceCount === "") return false;
  return /[^0-9.]/.test(sourceCount);
}
__name(activityHasScaledTargetCount, "activityHasScaledTargetCount");
function activityRequiresPostTemplateConfirmation(activity) {
  if (activity.target?.template?.type) {
    return true;
  }
  if (activityHasScaledTargetCount(activity)) return true;
  return false;
}
__name(activityRequiresPostTemplateConfirmation, "activityRequiresPostTemplateConfirmation");
function requiresTargetConfirmation(activity, options) {
  if (["none", "never"].includes(options.workflowOptions?.targetConfirmation ?? "")) return false;
  if (options.workflowOptions?.targetConfirmation === "always") return true;
  if (["enchant", "summon"].includes(activity.type)) return false;
  if (activity.target?.affects?.choice) return true;
  if (["", "self", void 0].includes(activity.target?.affects?.type) && ["", void 0, "self"].includes(activity.range?.units)) return false;
  if (activity.target?.affects.type === "self") return false;
  if (options.workflowOptions?.attackPerTarget === true) return false;
  if ((activity.forcedTargetConfirmation ?? activity.midiProperties?.confirmTargets) === "always") return true;
  if ((activity.forcedTargetConfirmation ?? activity.midiProperties?.confirmTargets) === "never") return false;
  if (targetConfirmation.noneTargeted && ((activity.target?.affects.type ?? "") !== "" || activity.attack) && (game.user?.targets?.size ?? 0) === 0)
    return true;
  let numTargets = game.user?.targets?.size ?? 0;
  if (numTargets === 0 && activity.item.type === "weapon" && shouldEnforceSingleTarget(activity))
    numTargets = 1;
  const token = tokenForActor(activity.actor);
  if (targetConfirmation.enabled) {
    if (targetConfirmation.always && (activity.target?.affects.type ?? "") !== "self") {
      if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.always");
      return true;
    }
    if (activity.attack && targetConfirmation.hasAttack) {
      if (debugEnabled > 0) warn("target confirmation triggered by targetConfirmation.hasAttack");
      return true;
    }
    if (activity.target?.affects.type === "creature" && targetConfirmation.hasCreatureTarget) {
      if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.hasCreatureTarget");
      return true;
    }
    if (targetConfirmation.noneTargeted && ((activity.target?.affects.type ?? "") !== "" || activity.attack) && numTargets === 0) {
      if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.noneTargeted");
      return true;
    }
    if (targetConfirmation.allies && token && numTargets > 0 && activity.target?.affects.type !== "self") {
      if (game.user?.targets.some((t) => t.document.disposition == token.document.disposition)) {
        if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.allies");
        return true;
      }
    }
    if (targetConfirmation.targetSelf && activity.target?.affects.type !== "self") {
      let tokenToUse = token;
      if (tokenToUse && game.user?.targets?.has(tokenToUse)) {
        if (debugEnabled > 0) warn("target confirmation triggered by has targetConfirmation.targetSelf");
        return true;
      }
    }
    if (targetConfirmation.mixedDisposition && numTargets > 0 && game.user?.targets) {
      const dispositions = /* @__PURE__ */ new Set();
      for (let target of game.user?.targets) {
        if (target) dispositions.add(target.document.disposition);
      }
      if (dispositions.size > 1) {
        if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.mixedDisposition");
        return true;
      }
    }
    if (targetConfirmation.longRange && game.user?.targets && numTargets > 0 && // @ts-expect-error no dnd5e-types
    (["ft", "m"].includes(activity.item.system.range?.units) || activity.item.system.range?.type === "touch")) {
      if (token) {
        for (let target of game.user?.targets) {
          const { result, attackingToken } = checkActivityRange(activity, token, /* @__PURE__ */ new Set([target]));
          if (result !== "normal") {
            if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.longRange");
            return true;
          }
        }
      }
    }
    if (targetConfirmation.inCover && numTargets > 0 && token && game.user?.targets) {
      const isRangeTargeting2 = ["ft", "m"].includes(activity.range?.units ?? "") && ["creature", "ally", "enemy"].includes(activity.target?.affects?.type ?? "");
      if (!activity.target?.template?.type && !isRangeTargeting2 && token) {
        for (let target of game.user?.targets) {
          if (computeCoverBonus(token, target, activity) > 0) {
            if (debugEnabled > 0) warn("target confirmation triggered from targetConfirmation.inCover");
            return true;
          }
        }
      }
    }
    const isRangeTargeting = ["ft", "m"].includes(activity.range?.units ?? "") && ["creature", "ally", "enemy"].includes(activity.target?.affects.type ?? "");
    if (activity.target?.template?.type && targetConfirmation.hasAoE) {
      if (debugEnabled > 0) warn("target confirmation triggered by targetConfirmation.hasAoE");
      return true;
    } else if (isRangeTargeting && targetConfirmation.hasRangedAoE) {
      if (debugEnabled > 0) warn("target confirmation triggered by has targetConfirmation.hasRangedAoE");
      return true;
    }
  }
  return false;
}
__name(requiresTargetConfirmation, "requiresTargetConfirmation");
async function preTemplateTargets(activity, options) {
  if (activityRequiresPostTemplateConfirmation(activity)) return true;
  if (requiresTargetConfirmation(activity, options))
    return await resolveTargetConfirmation(activity, options) === true;
  return true;
}
__name(preTemplateTargets, "preTemplateTargets");
async function postTemplateConfirmTargets(activity, options, workflow) {
  const updateTargets = /* @__PURE__ */ __name(async () => {
    const needCanSeeSense = game.user?.targets && !workflow.targets.equals(new Set(game.user?.targets));
    workflow.setTargets(new Set(game.user?.targets));
    if (needCanSeeSense) await workflow.activity.setupCanSeeSense({ workflow });
  }, "updateTargets");
  if (activityRequiresPostTemplateConfirmation(activity) && requiresTargetConfirmation(activity, options)) {
    const result = await resolveTargetConfirmation(activity, options);
    if (result && game.user?.targets) {
      await updateTargets();
    }
    return result === true;
  }
  if (activityRequiresPostTemplateConfirmation(activity) && game.user?.targets) {
    await updateTargets();
  }
  return true;
}
__name(postTemplateConfirmTargets, "postTemplateConfirmTargets");
async function resolveTargetConfirmation(activity, options = {}) {
  if (!canvas.tokens?.active) {
    await canvas.tokens?.activate();
  }
  const uiControls = ui.controls;
  const savedControl = uiControls?.control;
  const savedTool = uiControls?.tool;
  const tokenControl = uiControls?.controls.tokens;
  const tokenTargetTool = tokenControl?.tools?.target;
  await uiControls?.activate({ control: tokenControl.name, tool: tokenTargetTool.name });
  const wasMaximized = !activity.actor?.sheet?.minimized;
  if (wasMaximized) await activity.actor?.sheet?.minimize();
  let targets = new Promise((resolve, reject) => {
    options ??= {};
    options.callback = resolve;
    new TargetConfirmationDialog(activity.actor, activity, game.user, options).render({ force: true });
  });
  let shouldContinue = await targets;
  for (let token of canvas.tokens?.placeables ?? []) token.initializeVisionSource();
  if (savedTool) await ui.controls?.activate({ control: savedControl.name, tool: savedTool.name });
  if (wasMaximized) await activity.actor?.sheet?.maximize();
  return !!shouldContinue;
}
__name(resolveTargetConfirmation, "resolveTargetConfirmation");
async function showItemInfo() {
  const token = this.actor?.token;
  const templateData = {
    actor: this.actor,
    // tokenId: token?.id,
    tokenId: token?.uuid,
    tokenUuid: token?.uuid,
    item: this,
    itemUuid: this.uuid,
    // @ts-expect-error no dnd5e-types
    data: await this.system.getCardData(),
    // @ts-expect-error no dnd5e-types
    labels: this.labels,
    condensed: false,
    hasAttack: false,
    isHealing: false,
    hasDamage: false,
    // @ts-expect-error no dnd5e-types
    isSpell: this.type === "spell",
    isPower: false,
    hasSave: false,
    hasAreaTarget: false,
    hasAttackRoll: false,
    configSettings,
    hideItemDetails: false,
    hasEffects: false,
    isMerge: false
  };
  const templateType = ["tool"].includes(this.type) ? this.type : "item";
  const template = `modules/midi-qol/templates/${templateType}-card.hbs`;
  const html = await foundry.applications.handlebars.renderTemplate(template, templateData);
  const chatData = {
    content: html,
    // @ts-expect-error no dnd5e-types
    flavor: this.system.chatFlavor || this.name,
    speaker: getSpeaker(this.actor),
    flags: {
      "core": { "canPopout": true }
    }
  };
  chatData.style = CONST.CHAT_MESSAGE_STYLES.OTHER;
  let rollMode = safeGetGameSetting("core", "rollMode") ?? "public";
  if (["gmroll", "blindroll"].includes(rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").filter((u) => u.active);
  if (rollMode === "blindroll") chatData["blind"] = true;
  if (rollMode === "selfroll") chatData["whisper"] = [game.user?.id];
  return ChatMessage.create(chatData);
}
__name(showItemInfo, "showItemInfo");
function isTokenInside(template, token, wallsBlockTargeting) {
  const grid = canvas.scene?.grid;
  if (!grid) return false;
  const templatePos = { x: template.document.x, y: template.document.y };
  if (configSettings.optionalRules.wallsBlockRange !== "none" && hasWallBlockingCondition(token))
    return false;
  if (!isValidTarget(token)) return false;
  const startX = token.document.width >= 1 ? 0.5 : token.document.width / 2;
  const startY = token.document.height >= 1 ? 0.5 : token.document.height / 2;
  for (let x = startX; x < token.document.width; x++) {
    for (let y = startY; y < token.document.height; y++) {
      const currGrid = {
        x: token.x + x * grid.size - templatePos.x,
        y: token.y + y * grid.size - templatePos.y
      };
      let contains = template.shape?.contains(currGrid.x, currGrid.y);
      if (contains && wallsBlockTargeting) {
        let tx = templatePos.x;
        let ty = templatePos.y;
        if (template.shape instanceof PIXI.Rectangle) {
          tx = tx + template.shape.width / 2;
          ty = ty + template.shape.height / 2;
        }
        const r = new foundry.canvas.geometry.Ray({ x: tx, y: ty }, { x: currGrid.x + templatePos.x, y: currGrid.y + templatePos.y });
        if (configSettings.optionalRules.wallsBlockRange === "centerLevels" && installedModules.get("levels") && !installedModules.get("levelsvolumetrictemplates")) {
          let p1 = {
            x: currGrid.x + templatePos.x,
            y: currGrid.y + templatePos.y,
            z: token.document.elevation
          };
          const { elevation } = CONFIG.Levels.handlers.TemplateHandler.getTemplateData(false);
          let p2 = {
            x: tx,
            y: ty,
            z: elevation
          };
          contains = getUnitDist(p2.x, p2.y, p2.z, token) <= template.document.distance;
          contains = contains && !CONFIG.Levels?.API?.testCollision(p1, p2, "collision");
        } else if (!installedModules.get("levelsvolumetrictemplates")) {
          contains = !CONFIG.Canvas.polygonBackends.sight.testCollision({ x: tx, y: ty }, { x: currGrid.x + templatePos.x, y: currGrid.y + templatePos.y }, { mode: "any", type: "move" });
        }
      }
      if (contains) return true;
    }
  }
  return false;
}
__name(isTokenInside, "isTokenInside");
function isAoETargetable(targetToken, options = { ignoreSelf: false, AoETargetType: "any" }) {
  if (!isValidTarget(targetToken)) return false;
  const autoTarget = options.autoTarget ?? configSettings.autoTarget;
  const selfToken = getToken(options.selfToken);
  if (["wallsBlockIgnoreIncapacitated", "alwaysIgnoreIncapacitated"].includes(autoTarget) && checkIncapacitated(targetToken.actor, false, false)) return false;
  if (["wallsBlockIgnoreDefeated", "alwaysIgnoreDefeated"].includes(autoTarget) && checkDefeated(targetToken)) return false;
  if (targetToken.document.uuid === selfToken?.document.uuid && options.ignoreSelf) return false;
  const selfDisposition = selfToken?.document.disposition ?? 1;
  switch (options.AoETargetType) {
    case "any":
      return true;
    case "ally":
      return targetToken.document.disposition === selfDisposition;
    case "notAlly":
      return targetToken.document.disposition !== selfDisposition;
    case "enemy":
      return targetToken.document.disposition === -selfDisposition || targetToken.document.disposition == CONST.TOKEN_DISPOSITIONS.SECRET;
    case "notEnemy":
      return targetToken.document.disposition !== -selfDisposition && targetToken.document.disposition !== CONST.TOKEN_DISPOSITIONS.SECRET;
    case "neutral":
      return targetToken.document.disposition === CONST.TOKEN_DISPOSITIONS.NEUTRAL;
    case "notNeutral":
      return targetToken.document.disposition !== CONST.TOKEN_DISPOSITIONS.NEUTRAL;
    case "friendly":
      return targetToken.document.disposition === CONST.TOKEN_DISPOSITIONS.FRIENDLY;
    case "notFriendly":
      return targetToken.document.disposition !== CONST.TOKEN_DISPOSITIONS.FRIENDLY;
    case "hostile":
      return targetToken.document.disposition === CONST.TOKEN_DISPOSITIONS.HOSTILE || targetToken.document.disposition == CONST.TOKEN_DISPOSITIONS.SECRET;
    case "notHostile":
      return targetToken.document.disposition !== CONST.TOKEN_DISPOSITIONS.HOSTILE && targetToken.document.disposition !== CONST.TOKEN_DISPOSITIONS.SECRET;
    default:
      return true;
  }
}
__name(isAoETargetable, "isAoETargetable");
function selectTargetsFromTemplates(templates, selfTokenRef = "", ignoreSelf = false, AoETargetType = "any", autoTarget) {
  const tokens = computeTargetsFromTemplates(templates, selfTokenRef, ignoreSelf, AoETargetType, autoTarget);
  const targetIds = tokens.map((t) => t.id).filter(Boolean);
  updateUserTargets(targetIds);
  return tokens;
}
__name(selectTargetsFromTemplates, "selectTargetsFromTemplates");
function computeTargetsFromTemplate(template, selfTokenRef = "", ignoreSelf = false, AoETargetType = "any", autoTarget) {
  if (!autoTarget) autoTarget = configSettings.autoTarget;
  if (autoTarget === "none") return [];
  let mTemplate;
  if (typeof template === "string") {
    const tDoc = fromUuidSync(template);
    mTemplate = tDoc?.object ?? void 0;
  } else if (template instanceof MeasuredTemplateDocument) {
    mTemplate = template.object ?? void 0;
  } else {
    mTemplate = template;
  }
  if (!mTemplate) return [];
  const useWalledTemplates = autoTarget === "walledtemplates" && game.modules.get("walledtemplates")?.active;
  if (!useWalledTemplates) {
    const templateType = mTemplate.document.t;
    if (templateType === "cone" || templateType === "ray" || !mTemplate.shape) {
      const { shape } = computeTemplateShapeDistance(mTemplate.document);
      mTemplate.shape = shape;
    }
  }
  const selfToken = getToken(selfTokenRef);
  let targetTokens = [];
  if (installedModules.get("levelsvolumetrictemplates")) {
    const tokensToCheck = canvas.tokens?.placeables?.filter(
      (tk) => isAoETargetable(tk, { selfToken, ignoreSelf, AoETargetType, autoTarget })
    );
    VolumetricTemplates.compute3Dtemplate(mTemplate, tokensToCheck);
    targetTokens = Array.from(game.user?.targets ?? []);
  } else if (useWalledTemplates) {
    targetTokens = mTemplate.targetsWithinShape ? mTemplate.targetsWithinShape() : [];
    targetTokens = targetTokens.filter((token) => isAoETargetable(token, { selfToken, ignoreSelf, AoETargetType, autoTarget }));
  } else {
    const wallsBlockTargeting = ["wallsBlock", "wallsBlockIgnoreDefeated", "wallsBlockIgnoreIncapacitated"].includes(autoTarget);
    for (const token of canvas.tokens?.placeables ?? []) {
      if (!isAoETargetable(token, { selfToken, ignoreSelf, AoETargetType, autoTarget })) continue;
      if (token.actor && isTokenInside(mTemplate, token, wallsBlockTargeting)) {
        if (token.id) {
          targetTokens.push(token);
        }
      }
    }
  }
  return targetTokens;
}
__name(computeTargetsFromTemplate, "computeTargetsFromTemplate");
function computeTargetsFromTemplates(templates, selfTokenRef = "", ignoreSelf = false, AoETargetType = "any", autoTarget) {
  const allTargetTokens = /* @__PURE__ */ new Set();
  for (const template of templates) {
    const tokens = computeTargetsFromTemplate(template, selfTokenRef, ignoreSelf, AoETargetType, autoTarget);
    tokens.forEach((t) => allTargetTokens.add(t));
  }
  return Array.from(allTargetTokens);
}
__name(computeTargetsFromTemplates, "computeTargetsFromTemplates");
function getSceneTargets() {
  return globalThis.dnd5e.utils.getSceneTargets();
}
__name(getSceneTargets, "getSceneTargets");

// src/module/activities/MidiActivityMixin.ts
var MidiConditionField = class extends globalThis.dnd5e.dataModels.fields.FormulaField {
  static {
    __name(this, "MidiConditionField");
  }
  _validateType(value) {
    return typeof value === "string";
  }
  constructor(options) {
    super(options);
  }
  static get _defaults() {
    return foundry.utils.mergeObject(super._defaults, {
      deterministic: false
    });
  }
  /**
   * Override _toInput to wrap the text input with a condition editor button.
   * The button opens the ConditionEditor which provides autocomplete for condition expressions.
   */
  _toInput(config) {
    const input = super._toInput(config);
    if (!configSettings.conditionEditorEnabled) return input;
    const wrapper = document.createElement("div");
    wrapper.classList.add("midi-condition-field-wrapper");
    wrapper.appendChild(input);
    const fieldName = config.name ?? this.name;
    const fieldLabel = this.label ?? config.name ?? this.name;
    const button = document.createElement("button");
    button.type = "button";
    button.classList.add("midi-condition-editor-btn", "unbutton", "control-button");
    button.dataset.action = "openConditionEditor";
    button.dataset.fieldName = fieldName;
    button.dataset.fieldLabel = fieldLabel;
    button.innerHTML = '<i class="fas fa-code" inert></i>';
    button.title = i18n("midi-qol.ConditionEditor.Title");
    wrapper.appendChild(button);
    return wrapper;
  }
};
var MidiActivityMixin = /* @__PURE__ */ __name((Base) => {
  ;
  class MidiActivityMixin2 extends Base {
    static {
      __name(this, "MidiActivityMixin");
    }
    get workflow() {
      warn("MidiActivityMixin | activity.workflow is deprecated. activities no long store workflow references");
      return Workflow.getWorkflowByActivityUuid(this.uuid);
    }
    set workflow(value) {
      error2("MidiActivityMixin | activity.workflow = is deprecated. activities no long store workflow references");
      ``;
    }
    get targets() {
      foundry.utils.logCompatibilityWarning("MidiActivityMixin | activity.targets is deprecated. Use workflow.targets instead");
      return Workflow.getWorkflowByActivityUuid(this.uuid)?.targets ?? /* @__PURE__ */ new Set();
    }
    set targets(value) {
      foundry.utils.logCompatibilityWarning("MidiActivityMixin | setting activity.targets is not supported. Use workflow.targets instead");
    }
    /** The effective activation type for this activity. Overridden by MidiCastActivity to resolve from the linked spell. */
    get effectiveActivationType() {
      return this.activation?.type;
    }
    static LOCALIZATION_PREFIXES = [...super.LOCALIZATION_PREFIXES, "midi-qol.SHARED"];
    static defineSchema() {
      const { StringField: StringField2, ArrayField, BooleanField, NumberField, SchemaField, ObjectField, SetField } = foundry.data.fields;
      const { FormulaField } = globalThis.dnd5e.dataModels.fields;
      const schema = {
        ...super.defineSchema(),
        // flags: new ObjectField(),
        useConditionText: new MidiConditionField({ name: "useCondition", initial: "" }),
        useConditionReason: new StringField2({ name: "useConditionReason", initial: "" }),
        effectConditionText: new MidiConditionField({ name: "effectCondition", initial: "" }),
        // disabled pending a way to make it work 
        // useSystemActivity: new BooleanField({ name: "useSystemActivity", initial: false }),
        macroData: new SchemaField({
          name: new StringField2({ name: "name", initial: "" }),
          command: new StringField2({ name: "command", initial: "" })
        }),
        ignoreTraits: new SchemaField({
          idi: new BooleanField({ name: "idi", initial: false }),
          idr: new BooleanField({ name: "idr", initial: false }),
          idv: new BooleanField({ name: "idv", initial: false }),
          ida: new BooleanField({ name: "ida", initial: false }),
          idm: new BooleanField({ name: "idm", initial: false })
        }),
        midiProperties: new SchemaField({
          ignoreTraits: new SetField(new StringField2(), { initial: [] }),
          triggeredActivityId: new StringField2({ name: "triggeredActivity", initial: "none", required: true, blank: false }),
          //@ts-expect-error
          triggeredActivityConditionText: new MidiConditionField({ name: "triggeredActivityCondition", initial: "" }),
          triggeredActivityTargets: new StringField2({ name: "triggeredActivityTargets", initial: "targets", required: true, blank: false }),
          triggeredActivityRollAs: new StringField2({ name: "triggeredActivityRollAs", initial: "self" }),
          triggeredActivityConsume: new BooleanField({ name: "triggeredActivityConsume", initial: true }),
          triggeredActivityConfigure: new BooleanField({ name: "triggeredActivityConfigure", initial: true }),
          autoConsume: new BooleanField({ name: "autoConsume", initial: false }),
          forceConsumeDialog: new StringField2({ name: "forceConsumeDialog", initial: "default" }),
          forceRollDialog: new StringField2({ name: "forceRollDialog", initial: "default" }),
          forceDamageDialog: new StringField2({ name: "forceDamageDialog", initial: "default" }),
          confirmTargets: new StringField2({ name: "confirmTargets", initial: "default", required: true, blank: false }),
          autoTargetType: new StringField2({ name: "autoTargetType", initial: "any", required: true, blank: false }),
          autoTargetAction: new StringField2({ name: "autoTargetAction", initial: "default" }),
          automationOnly: new BooleanField({ name: "automationOnly", initial: false }),
          otherActivityCompatible: new BooleanField({ name: "otherActivityCompatible", initial: true }),
          otherActivityAsParentType: new BooleanField({ name: "otherActivityAsParentType", initial: true, required: false }),
          identifier: new StringField2({ name: "identifier", initial: "", required: false }),
          displayActivityName: new BooleanField({ name: "displayActivityName", initial: false }),
          rollMode: new StringField2({ name: "rollMode", initial: "default", required: true, blank: false }),
          chooseEffects: new BooleanField({ name: "chooseEffects", initial: false }),
          toggleEffect: new BooleanField({ name: "toggleEffect", initial: false }),
          ignoreFullCover: new BooleanField({ name: "ignoreFullCover", initial: false }),
          removeChatButtons: new StringField2({ name: "removeChatButtons", initial: "default", blank: false, required: true }),
          magicEffect: new BooleanField({ name: "magicEffect", initial: false }),
          magicDamage: new BooleanField({ name: "magicDamage", initial: false }),
          noConcentrationCheck: new BooleanField({ name: "noConcentrationCheck", initial: false }),
          skipConcentrationCheck: new BooleanField({ name: "skipConcentrationCheck", initial: false }),
          autoCEEffects: new StringField2({ name: "autoCEEffects", initial: "default", blank: false, required: true })
        }),
        isOverTimeFlag: new BooleanField({ name: "isOverTimeFlag", initial: false }),
        overTimeProperties: new SchemaField({
          turnChoice: new StringField2({ blank: false, choices: ["start", "end"], default: "start" }),
          saveRemoves: new BooleanField({ initial: true }),
          rollAs: new StringField2({ blank: false, choices: ["target", "source"], initial: "target" }),
          //@ts-expect-error find out why there is a type error here
          preRemoveConditionText: new MidiConditionField({ name: "removeCondition", initial: "" }),
          //@ts-expect-error find out why there is a type error here
          postRemoveConditionText: new MidiConditionField({ name: "postRemoveCondition", initial: "" })
        })
      };
      return schema;
    }
    /**
     * @type {boolean}
     * @memberof MidiActivityMixin
     * @readonly
     * @returns {boolean}
     * @description Is this activity suitable as an other activity. It must be a possible other activity (default false) and the otherActivityCompatible flag must be set to true
     */
    get isOtherActivityCompatible() {
      if (!this.possibleOtherActivity) return false;
      return this.midiProperties.otherActivityCompatible;
    }
    /**
     * @type {boolean}
     * @memberof MidiActivityMixin
     * @readonly
     * @returns {boolean}
     * @description Is this activity suitable as a triggerable activity. Default is true and must be overridden in the subclass
     * Examples of non triggerable activities are the Enchant activity
     */
    get isTriggerableActivity() {
      return true;
    }
    /**
     * @type {boolean}
     * @memberof MidiActivityMixin
     * @readonly
     * @returns {boolean}
     * @description Is this activity only triggerable by the actor that owns it. 
     * Default is false and must be overridden in the subclass
     * Examples of self triggerOnly are the Cast and Forward activities
     */
    get isSelfTriggerableOnly() {
      return false;
    }
    get forcedTargetConfirmation() {
      return void 0;
    }
    /**
     * @type {boolean}
     * @memberof MidiActivityMixin
     * @readonly
     * @returns {boolean}
     * @description Is this activity a candidate to be used as an "other" activity. Default is false and must be overridden in the subclass
     */
    get possibleOtherActivity() {
      return false;
    }
    get canUseOtherActivity() {
      return false;
    }
    get canUse() {
      return super.canUse;
    }
    getOnUseMacros({ onlyOnUseItemMacros = false } = {}) {
      const onUseMacros = new OnUseMacros();
      this.ammunitionOnUseMacros = new OnUseMacros();
      const itemOnUseMacros = this.item?.flags?.[MODULE_ID]?.onUseMacroParts ?? new OnUseMacros();
      const ammunitionOnUseMacros = this.ammunitionItem?.flags?.[MODULE_ID]?.onUseMacroParts ?? new OnUseMacros();
      const actorOnUseMacros = this.actor?.flags?.[MODULE_ID]?.onUseMacroParts ?? new OnUseMacros();
      if (onlyOnUseItemMacros) {
        onUseMacros.items = [...itemOnUseMacros.items];
      } else {
        onUseMacros.items = [...itemOnUseMacros.items, ...actorOnUseMacros.items];
      }
      this.ammunitionOnUseMacros.items = ammunitionOnUseMacros.items;
      return onUseMacros;
    }
    get validProperties() {
      return /* @__PURE__ */ new Set(["idi", "idr", "idv", "ida"]);
    }
    get macro() {
      return new Macro({ name: this.macroData.name || this.name, command: this.macroData.command, img: this.img, type: "script" });
    }
    set macro(macro) {
      return this.update({ macroData: { name: macro.name, command: macro.command } });
    }
    get messageFlags() {
      const baseFlags = super.messageFlags;
      const targets = /* @__PURE__ */ new Map();
      if (this.workflow?.targets) {
        const dnd5eDescriptors = baseFlags.targets;
        for (const token of this.workflow.targets) {
          const { name } = token;
          const { img, system, uuid } = token.actor ?? {};
          if (!uuid) continue;
          const dnd5eTarget = dnd5eDescriptors?.find((td) => td.uuid === uuid);
          const ac = dnd5eTarget ? dnd5eTarget.ac : system?.attributes?.ac?.value ?? null;
          targets.set(uuid, { name, img, uuid, ac });
        }
        baseFlags.targets = Array.from(targets.values());
      }
      return baseFlags;
    }
    async getTriggeredActivity() {
      let activity = this.item.system.activities.find((a) => a.id === this.midiProperties?.triggeredActivityId);
      if (!activity) activity = this.item.system.activities.find((a) => a.identifier === this.midiProperties?.triggeredActivityId);
      if (!activity) activity = await fromUuid(this.midiProperties?.triggeredActivityId);
      return activity;
    }
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        usage: {
          dialog: MidiActivityUsageDialog,
          actions: {
            placeTemplate: MidiActivityMixin2.#placeTemplate,
            rollDamage: MidiActivityMixin2.#rollDamage,
            rollDamageNoCritical: MidiActivityMixin2.#rollDamageNoCritical,
            rollDamageCritical: MidiActivityMixin2.#rollDamageCritical,
            confirmDamageRollCancel: MidiActivityMixin2.#confirmDamageRollCancel,
            confirmDamageRollComplete: MidiActivityMixin2.#confirmDamageRollComplete,
            confirmDamageRollCompleteHit: MidiActivityMixin2.#confirmDamageRollCompleteHit,
            confirmDamageRollCompleteMiss: MidiActivityMixin2.#confirmDamageRollCompleteMiss,
            midiApplyEffects: MidiActivityMixin2.#applyEffects,
            refundOtherActivity: MidiActivityMixin2.#refundOtherActivity,
            consumeOtherActivity: MidiActivityMixin2.#consumeOtherActivity
          }
        }
      },
      { inplace: false, insertValues: true, insertKeys: true }
    );
    static async #applyEffects(event, target, message) {
      const workflow = Workflow.getWorkflow(message.uuid);
      if (workflow) workflow.activity = this;
      if (!workflow) {
        const errMessage = "MidiQOL | MidiActivity | applyEffects | No workflow found";
        error2(errMessage);
        TroubleShooter.recordError(new Error("No workflow found"), errMessage);
        return;
      }
      const authorId = message.author?.id;
      if (game.user?.id !== authorId) {
        if (!game.user?.isGM) {
          ui.notifications?.warn("Only the GM can apply effects for other players");
          return;
        }
        if (game.user?.targets.size === 0) {
          ui.notifications?.warn(i18n("midi-qol.noTargets"));
          return;
        }
        await socketlibSocket.executeAsUser("applyEffects", authorId, {
          workflowId: message.uuid,
          targets: Array.from(game.user?.targets).map((t) => t.document.uuid)
        });
      } else {
        workflow.forceApplyEffects = true;
        workflow.effectTargets = new Set(game.user?.targets);
        if (workflow.effectTargets.size > 0) workflow.performState(workflow.WorkflowState_ApplyDynamicEffects);
      }
    }
    static async #confirmDamageRollCancel(event, target, message) {
      const workflowId = message.uuid;
      const authorId = message.author?.id;
      if (!authorId || !workflowId) return;
      if (!game.user?.isGM && configSettings.confirmAttackDamage === "gmOnly") {
        return;
      }
      const user = game.users?.get(authorId);
      if (user?.active) {
        await socketlibSocket.executeAsUser("cancelWorkflow", authorId, { workflowId, itemCardUuid: message.uuid }).then((result) => {
          if (typeof result === "string") ui.notifications?.warn(result);
        });
      } else {
        await Workflow.removeItemCardButtons(message.uuid, { removeAllButtons: true });
      }
    }
    static async #confirmDamageRollComplete(event, target, message) {
      await this.doConfirmation("confirmDamageRollComplete", event, target, message);
    }
    static async #confirmDamageRollCompleteHit(event, target, message) {
      await this.doConfirmation("confirmDamageRollCompleteHit", event, target, message);
    }
    static async #confirmDamageRollCompleteMiss(event, target, message) {
      await this.doConfirmation("confirmDamageRollCompleteMiss", event, target, message);
    }
    async doConfirmation(actionToCall, event, target, message) {
      if (!game.user?.isGM && configSettings.confirmAttackDamage === "gmOnly") {
        return;
      }
      if (message.author?.active) {
        const result = await socketlibSocket.executeAsUser(actionToCall, message.author.id, { workflowId: message.uuid, activityUuid: this.uuid, itemCardUuid: message.uuid });
        if (typeof result === "string") ui.notifications?.warn(result);
      } else {
        await Workflow.removeItemCardButtons(message.uuid, { removeConfirmButtons: true });
      }
    }
    static async #refundOtherActivity(event, target, message) {
      const activity = message.getAssociatedActivity?.();
      const otherActivity = activity?.otherActivity;
      if (!otherActivity) return;
      const consumed = message.getFlag(MODULE_ID, "use.consumed");
      if (!consumed || foundry.utils.isEmpty(consumed)) return;
      await otherActivity.refund(consumed);
      await message.unsetFlag(MODULE_ID, "use.consumed");
    }
    static async #consumeOtherActivity(event, target, message) {
      const activity = message.getAssociatedActivity?.();
      const otherActivity = activity?.otherActivity;
      if (!otherActivity) return;
      const otherUsageConfig = otherActivity._prepareUsageConfig({
        consume: { resources: true, spellSlot: false, action: false }
      });
      const consumptionCheck = await otherActivity._prepareUsageUpdates(otherUsageConfig, { returnErrors: true });
      if (Array.isArray(consumptionCheck) && consumptionCheck.length > 0) {
        ui.notifications?.warn(i18nFormat2("midi-qol.otherActivityConsumptionFailed", {
          actorName: otherActivity.actor?.name ?? "Unknown",
          itemName: otherActivity.item?.name ?? "Unknown",
          activityName: otherActivity.name ?? "Unknown"
        }));
        return;
      }
      const messageConfig = { data: {} };
      const consumeResult = await otherActivity.consume(otherUsageConfig, messageConfig);
      if (consumeResult === false) {
        return;
      }
      const consumed = getConsumptionData(messageConfig);
      if (consumed) {
        await message.setFlag(MODULE_ID, "use.consumed", consumed);
      }
    }
    /**
     * Handle the "Place Template" button click from the chat card.
     * Places all templates sequentially and delegates to workflow for validation and processing.
     */
    static async #placeTemplate(event, target, message) {
      const AbilityTemplate = game.system.canvas.AbilityTemplate;
      const templates = [];
      try {
        const previews = AbilityTemplate.fromActivity(this);
        if (!previews) return;
        for (const template of previews) {
          const result = await template.drawPreview();
          if (result) {
            const docs = Array.isArray(result) ? result : [result];
            templates.push(...docs);
          }
        }
      } catch (err) {
      }
      const workflow = Workflow.getWorkflow(message.uuid);
      if (!workflow) {
        warn("MidiActivityMixin | #placeTemplate | No workflow found for message", message.uuid);
        return;
      }
      await workflow.processPlacedTemplates(templates, this);
    }
    static #rollDamage(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow, midiOptions: { isCritical: workflow?.workflowOptions?.isCritical || workflow?.isCritical } }, {}, message);
    }
    static #rollDamageNoCritical(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow, critical: { allow: false }, midiOptions: { isCritical: false } }, {}, message);
    }
    static #rollDamageCritical(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow, critical: { allow: true }, midiOptions: { isCritical: true } }, {}, message);
    }
    get identifier() {
      if (this.midiProperties?.identifier) return this.midiProperties.identifier;
      return this.name.slugify();
    }
    _preCreate(data) {
      super._preCreate(data);
      if (!("otherActivityId" in data)) {
        this.updateSource({ otherActivityId: "none" });
      }
    }
    prepareData() {
      if (!this.midiProperties.identifier && !this.name) this.midiProperties.identifier = i18n(this.constructor.metadata.dnd5eTitle).slugify();
      super.prepareData();
    }
    async _onChatAction(event, target, message) {
      const existingItem = this.workflow?.item ?? this.item;
      const activity = this.workflow?.activity ?? existingItem.system.activities.get(this.id);
      const action = target.dataset.action ?? "";
      let handler = activity?.metadata.usage?.actions?.[action];
      const workflow = Workflow.getWorkflow(message.uuid);
      if (!handler && workflow) {
        if (activity) handler = activity?.metadata.usage?.actions?.[action];
      }
      if (handler) await handler.call(activity, event, target, message);
    }
    async useAs(actor, usage = {}, dialog = {}, message = {}) {
      actor = getActor(usage.midiOptions?.rollAs);
      if (usage.midiOptions?.rollAs) delete usage.midiOptions.rollAs;
      if (!actor || actor === this.actor || this.isSelfTriggerableOnly) return this.use(usage, dialog, message);
      const itemData = this.item.toObject();
      delete itemData._id;
      foundry.utils.setProperty(itemData, `flags.${MODULE_ID}.syntheticItem`, true);
      let item = new CONFIG.Item.documentClass(itemData, { parent: actor });
      item.prepareData();
      item.prepareFinalAttributes();
      const activity = item.system.activities.get(this.id);
      return activity.use(usage, dialog, message);
    }
    async use(usage = {}, dialog = {}, message = {}) {
      if (usage.midiOptions?.rollAs) return this.useAs(usage.midiOptions.rollAs, usage, dialog, message);
      if (!this.item.isEmbedded) return;
      if (!this.item.isOwner) {
        ui.notifications?.error("DND5E.DocumentUseWarn", { localize: true });
        return;
      }
      if (!this.canUse) {
        ui.notifications?.error("DND5E.ACTIVITY.Warning.UsageNotAllowed", { localize: true });
        return;
      }
      let preUseActivityHookId;
      let activityConsumptionHookId;
      try {
        usage.midiOptions ??= {};
        usage.midiOptions.workflowOptions ??= {};
        usage.midiOptions.workflowOptions.preSelectedTargetUuids ??= Array.from(game.user?.targets ?? []).map((t) => t.document.uuid);
        if (debugEnabled > 0) warn("MidiQOL | MidiActivity | use | Called", usage, dialog, message);
        let item = this.item.clone({}, { keepId: true });
        let activity = item.system.activities.get(this.id);
        let workflowClass = usage?.midi?.workflowClass ?? globalThis.MidiQOL.workflowClass;
        if (game.user?.isGM && checkRule("activeDefence")) {
          workflowClass = ActiveDefenceWorkflow;
        }
        if (!(workflowClass.prototype instanceof Workflow)) workflowClass = Workflow;
        const workflowOptions = { ...usage.midiOptions, ...usage.midiOptions?.workflowOptions ?? {}, event: usage.event, storeWorkflow: true };
        delete workflowOptions.workflowOptions;
        if (usage.workflow)
          usage.midiOptions.workflowOptions.autoRollAttack = true;
        if (!(usage.workflow instanceof TrapWorkflow)) {
          if (configSettings.savesBeforeDamage)
            usage.workflow = new SavesFirstWorkflow(activity.actor ?? null, activity, ChatMessage.getSpeaker({ actor: activity.item.actor }), null, { workflowOptions });
          else usage.workflow = new workflowClass(activity.actor ?? null, activity, ChatMessage.getSpeaker({ actor: activity.item.actor }), null, { workflowOptions });
        }
        usage.workflow.sequenceId = usage.sequenceId;
        message.workflow = usage.workflow;
        usage.workflow.systemCard = message.systemCard;
        if ((activity.midiProperties?.rollMode ?? "default") !== "default") message.rollMode = activity.midiProperties.rollMode;
        if (activity.midiProperties?.skipConcentrationCheck && this.actor && activity.consumption.targets.some((t) => t.target === "attributes.hp.value"))
          skipConcentrationActorUuids.add(this.actor.uuid);
        if (item.parent) await removeFlanking(item.parent);
        if (!usage.workflow) return void 0;
        if (usage.midiOptions.isCritical) {
          usage.workflow.isCritical = true;
          usage.workflow.attackRollModifierTracker.addAttribution("CRIT", "activity.use", i18n("midi-qol.ActivityUse"));
        }
        if (usage.midiOptions.isFumble) {
          usage.workflow.isFumble = true;
          usage.workflow.attackRollModifierTracker.addAttribution("FUMBLE", "activity.use", i18n("midi-qol.ActivityUse"));
        }
        const autoCreateTemplate = activityHasAutoPlaceTemplate(activity);
        const emanationNoTemplate = activityHasEmanationNoTemplate(activity);
        const isTrapWorkflow = usage.workflow instanceof TrapWorkflow;
        if (autoCreateTemplate || emanationNoTemplate || isTrapWorkflow) {
          usage.create ??= {};
          usage.create.measuredTemplate = false;
        }
        foundry.utils.setProperty(message, "data.flags.midi-qol.messageType", "attack");
        if (usage.midiOptions?.configureDialog === false) dialog.configure = false;
        if (!await activity.checkAutoConsume(usage, dialog)) {
          if (this.midiProperties?.forceConsumeDialog === "always") dialog.configure = true;
          else if (this.midiProperties?.forceConsumeDialog === "never") dialog.configure = false;
        }
        const rollToggle = areMidiKeysPressed(usage.event, "RollToggle");
        if (rollToggle && usage.workflow) {
          foundry.utils.setProperty(usage.workflow, "rollOptions.rollToggle", true);
          dialog.configure = !dialog.configure;
        }
        if (!activity.item._id || foundry.utils.getProperty(activity.item, `flags.${MODULE_ID}.syntheticItem`))
          foundry.utils.setProperty(message, "data.flags.dnd5e.item.data", activity.item.toObject());
        let useResults;
        preUseActivityHookId = Hooks.on("dnd5e.preUseActivity", (newActivity, usageConfig, messageConfig) => {
          if (usage.workflow.activity?.uuid !== newActivity.uuid) return true;
          usageConfig.workflow = usage.workflow;
          usage.workflow.activity = newActivity;
          messageConfig.workflow = usage.workflow;
          return true;
        });
        let usageData;
        activityConsumptionHookId = Hooks.on("dnd5e.activityConsumption", (newActivity, usageConfig, messageConfig) => {
          if (usage.workflow.activity?.uuid !== newActivity.uuid) return;
          usageData = usageConfig;
          usageConfig.workflow.activity = newActivity;
          usageConfig.activity = newActivity;
        });
        if (!await activity.preTargetingChecks(usage, dialog, message)) return false;
        const isSelfTarget = this.target?.affects?.type === "self" || this.item.type === "tool" && !this.target?.affects?.type;
        if (isSelfTarget) {
          pushTargetSave();
          usage.workflow.selfTargeted = true;
        } else {
          clearTargetSave();
        }
        if (!await activity.setupTargets(usage, dialog, message)) {
          if (isSelfTarget) {
            popTargetSave();
            usage.workflow.selfTargeted = false;
          }
          ui.notifications?.warn(i18n("midi-qol.noTargets"));
          return false;
        }
        if (!await activity.preChatChecks(usage, dialog, message)) {
          if (isSelfTarget) {
            popTargetSave();
            usage.workflow.selfTargeted = false;
          }
          return false;
        }
        if (usage.midiOptions?.spellLevel && activity.item?.type === "spell") {
          usage.spell ??= {};
          usage.spell.slot = `spell${usage.midiOptions.spellLevel}`;
        }
        try {
          useResults = await super.use.bind(activity)(usage, dialog, message);
        } catch (err) {
          error2("MidiQOL | MidiActivity | use | Error in super.use", err);
          TroubleShooter.recordError(err, "Error in super.use");
          Workflow.removeWorkflow(usage.workflow?.id);
          return false;
        }
        if (this.actor) skipConcentrationActorUuids.delete(this.actor.uuid);
        if (!useResults) {
          Workflow.removeWorkflow(usage.workflow?.id);
          return false;
        }
        Workflow.workflows.delete(usage.workflow.activity?.uuid ?? "");
        usage.workflow.itemCardUuid = useResults.message.uuid;
        usage.workflow.id = useResults.message.uuid ?? usage.workflow.id;
        usage.workflow.activity = usage.workflow.activity;
        usage.workflow.item = usage.workflow.activity?.item;
        activity = usage.workflow.activity;
        Workflow.addWorkflow(usage.workflow);
        if (await activity.postChatCardChecks(usage, dialog, message) !== true) return;
        await activity.setupCanSeeSense(usage);
        if (activity.templates) {
          useResults.templates = activity.templates;
          delete activity.templates;
        }
        const allTemplates = (useResults.templates ?? []).flat();
        if (allTemplates.length > 0) {
          await usage.workflow.processPlacedTemplates(allTemplates, activity);
        }
        if (configSettings.undoWorkflow) await saveUndoData(usage.workflow);
        usage.workflow.itemUseComplete = true;
        usage.workflow.id = useResults.message.uuid;
        if ((usage.workflow.itemUsesReaction || usage.midiOptions?.workflowOptions?.castUsesReaction) && this.actor && !hasUsedReaction(this.actor))
          await setReactionUsed(this.actor);
        if ((usage.workflow.itemUsesBonusAction || usage.midiOptions?.workflowOptions?.castUsesBonusAction) && this.actor && !hasUsedBonusAction(this.actor))
          await setBonusActionUsed(this.actor);
        if (activity.item?.type === "spell" || activity.item.flags?.dnd5e?.spellLevel) {
          usage.workflow.castData = {
            // @ts-expect-error no dnd5e-types
            baseLevel: activity.item.system.level ?? activity.item.flags?.dnd5e?.spellLevel?.base,
            // @ts-expect-error no dnd5e-types
            castLevel: activity.item.system.level !== void 0 ? activity.item.system.level + (usageData?.scaling ?? 0) : activity.item.flags?.dnd5e?.spellLevel?.value,
            scaling: (usageData?.scaling ?? activity.item.flags?.dnd5e?.scaling) || 0,
            itemUuid: activity.item.uuid
          };
        }
        await usage.workflow.performState(usage.workflow.WorkflowState_Start, {});
        return useResults;
      } finally {
        if (preUseActivityHookId) Hooks.off("dnd5e.preUseActivity", preUseActivityHookId);
        if (activityConsumptionHookId) Hooks.off("dnd5e.activityConsumption", activityConsumptionHookId);
      }
    }
    /**
     * Check if this activity has consumption configured.
     * @returns true if this activity has consumption targets configured
     */
    hasConsumption() {
      return (this.consumption?.targets?.length ?? 0) > 0;
    }
    /**
     * Determines whether this activity should auto-consume resources without showing a dialog.
     * @param workflow - The workflow for getting auto-consume settings
     * @returns true if consumption should be automatic, false if a dialog should be shown
     */
    shouldAutoConsume(workflow) {
      const forceConsumeDialog = this.midiProperties?.forceConsumeDialog ?? "default";
      if (forceConsumeDialog === "always") {
        return false;
      }
      if (forceConsumeDialog === "never" || this.midiProperties?.autoConsume) {
        return true;
      }
      const autoConsumeSetting = autoConsumeResource(workflow);
      const isSpell = this.item?.type === "spell";
      if (autoConsumeSetting === "none") {
        return false;
      }
      if (autoConsumeSetting === "spell" && !isSpell) {
        return false;
      }
      if (autoConsumeSetting === "item" && isSpell) {
        return false;
      }
      return true;
    }
    async checkAutoConsume(usage, dialog = {}) {
      if (dialog.configure === true) return false;
      if (!this.shouldAutoConsume(usage.workflow)) return false;
      const consumption = this._prepareUsageConfig(usage);
      const autoConsume = await this._prepareUsageUpdates(consumption, { returnErrors: true });
      if ((autoConsume.length ?? 0) > 0) {
        dialog.configure = true;
        return true;
      }
      dialog.configure = false;
      if (this.hasAreaTarget && this.actor?.sheet) {
        setTimeout(() => {
          this.actor?.sheet?.minimize();
        }, 100);
      }
      return true;
    }
    async rollDamage(config, dialog = {}, message = {}) {
      if (!config?.workflow) {
        return super.rollDamage(config, dialog, message);
      }
      if (config.workflow.currentAction === config.workflow.WorkflowState_Abort || config.workflow.currentAction === config.workflow.WorkflowState_Completed)
        return this.use(config, dialog, {});
      config.midiOptions ??= config.workflow.workflowOptions ?? {};
      if (debugEnabled > 0) {
        warn("MidiActivity | rollDamage | Called", config, dialog, message);
      }
      let result;
      let otherResult;
      let preRollDamageHookId;
      let rollDamageHookId;
      try {
        if (await callActivityHooks(this, "midi-qol.preDamageRoll", config.workflow, this, config, dialog, message) === false) {
          console.warn("midi-qol | Damage roll blocked via pre-hook");
          return;
        }
        if (configSettings.allowUseMacro) {
          const results = await config.workflow.callMacros(this.item, config.workflow.onUseMacros?.getMacros("preDamageRollConfig"), "OnUse", "preDamageRollConfig", { config, dialog, message });
          const cancelWorkflow2 = results?.some((i) => i === false) ?? false;
          if (cancelWorkflow2) {
            console.warn("midi-qol | Damage roll blocked by preDamageRollConfig onUseMacro");
            return;
          }
        }
        const areKeysPressed = game.system.utils.areKeysPressed;
        const keys = {
          normal: areKeysPressed(config.event, "skipDialogNormal") || areKeysPressed(config.event, "skipDialogDisadvantage"),
          critical: areKeysPressed(config.event, "skipDialogAdvantage")
        };
        config.midiOptions.isCritical ??= config.workflow.workflowOptions?.isCritical || config.workflow.isCritical;
        config.isCritical ??= config.midiOptions.isCritical;
        config.midiOptions.fastForwardDamage ??= config.workflow.workflowOptions?.fastForwardDamage;
        if (this.hasDamage || this.hasHealing) {
          if (Object.values(keys).some((k) => k)) dialog.configure = this.midiProperties?.forceDamageDialog !== "never";
          else if (dialog.configure === void 0) {
            switch (this.midiProperties?.forceDamageDialog) {
              case "always":
                dialog.configure = true;
                break;
              case "never":
                dialog.configure = false;
                break;
              default:
                dialog.configure = !(config.midiOptions?.fastForwardDamage ?? config.workflow?.workflowOptions.fastForwardDamage ?? false);
            }
          }
          if (config.workflow && areMidiKeysPressed(config.event, "RollToggle")) config.workflow.rollOptions.rollToggle = !config.workflow.rollOptions.rollToggle;
          if (this.item.isVersatile && areMidiKeysPressed(config.event ?? config.workflow.rollOptions.event, "Versatile")) {
            config.workflow.attackMode = "twoHanded";
          }
          if (config.workflow.rollOptions?.rollToggle) dialog.configure = !dialog.configure;
          preRollDamageHookId = Hooks.once("dnd5e.preRollDamage", (rollConfig, dialogConfig, messageConfig) => {
            const targetDescriptors = foundry.utils.getProperty(messageConfig, "data.flags.dnd5e.targets");
            if (targetDescriptors && config.workflow) {
              config.workflow.targetDescriptors ??= [];
              for (let targetDetails of targetDescriptors) {
                const existing = config.workflow.targetDescriptors.find((td) => td.uuid === targetDetails.uuid);
                if (existing) {
                  Object.assign(existing, foundry.utils.mergeObject(existing, targetDetails));
                } else {
                  config.workflow.targetDescriptors.push(targetDetails);
                }
              }
            }
            if (keys.critical) rollConfig.isCritical = true;
            else if (keys.normal) rollConfig.isCritical = false;
            else if (!dialogConfig.configure) rollConfig.isCritical ||= rollConfig.midiOptions?.isCritical;
            if (this.damage?.parts.some((part) => part.types.size > 1)) dialogConfig.configure = true;
            else if ((rollConfig.ammunition?.system.damage.base?.types.size ?? 0) > 1) dialogConfig.configure = true;
            else if ((this.healing?.types?.size ?? 0) > 1) dialogConfig.configure = true;
            if (dialogConfig.configure) {
              dialogConfig.options ??= {};
              if (rollConfig.isCritical || rollConfig.midiOptions?.isCritical) {
                dialogConfig.options.defaultButton = "critical";
              } else dialogConfig.options.defaultButton = "normal";
            }
            return true;
          });
          rollDamageHookId = Hooks.once("dnd5e.rollDamage", (rolls) => {
            if (rolls[0] && config.workflow && config.midiOptions?.updateWorkflow !== false)
              config.workflow.isCritical = rolls[0].options.isCritical;
          });
          const rollMode = safeGetGameSetting("core", "rollMode");
          message.create ??= false;
          const showChatDamageCard = game.user?.isGM && configSettings.gmAttackDamageCards && [CONST.DICE_ROLL_MODES.SELF, CONST.DICE_ROLL_MODES.BLIND, CONST.DICE_ROLL_MODES.PRIVATE].includes(rollMode);
          if (showChatDamageCard) {
            message.create = true;
            foundry.utils.setProperty(message, "data.flags.midi-qol.gmHide", true);
          }
          result = await super.rollDamage(config, dialog, message);
          if (!result) {
            config.workflow.currentAction = config.workflow.WorkflowState_WaitForDamageRoll;
            return;
          }
          if (result) result = await this.postProcessDamageRoll(config, result);
          if (config.workflow && config.midiOptions?.updateWorkflow !== false) await config.workflow.setDamageRolls(result);
        }
        if (this.otherActivity && config.workflow.otherActivity !== this && config.midiOptions?.updateWorkflow !== false) {
          if (!this.deferOtherDamage(config, dialog)) {
            otherResult = await this.rollOtherDamage(config, dialog);
          }
        }
        if (config.midiOptions?.updateWorkflow !== false && config.workflow.suspended) {
          const noDamage = !result?.[0] && !otherResult?.[0] && !(this.hasDamage || this.hasHealing);
          config.workflow.unSuspend.bind(config.workflow)({ damageRoll: result?.[0], otherDamageRoll: otherResult?.[0], noDamage });
        }
      } catch (err) {
        const message2 = "doDamageRoll error";
        TroubleShooter.recordError(err, message2);
        error2(message2, err);
      } finally {
        if (preRollDamageHookId) Hooks.off("dnd5e.preRollDamage", preRollDamageHookId);
        if (rollDamageHookId) Hooks.off("dnd5e.rollDamage", rollDamageHookId);
      }
      return result ?? [];
    }
    /**
     * Check if other activity damage should be deferred until after consumption is confirmed.
     * If deferral is needed, stores the config and dialog for later rolling.
     * Only defers if we're actually checking hits or saves - otherwise consume immediately.
     * @param config - The roll process configuration
     * @param dialog - The roll dialog configuration
     * @returns true if damage was deferred, false if it should be rolled immediately
     */
    deferOtherDamage(config, dialog) {
      if (!this.otherActivity || !config.workflow) return false;
      if (!this.otherActivity.hasConsumption()) return false;
      const checkingHits = this.attack && configSettings.autoCheckHit !== "none";
      const checkingSaves = (this.otherActivity.save || this.otherActivity.check) && configSettings.autoCheckSaves !== "none";
      if (!checkingHits && !checkingSaves) {
        return false;
      }
      config.workflow.otherDamageConfig = config;
      config.workflow.otherDamageDialog = { ...dialog };
      return true;
    }
    /**
     * Roll damage for the other activity referenced by this activity.
     * @param config - The roll process configuration
     * @param dialog - The roll dialog configuration
     * @returns The damage rolls from the other activity, or undefined if not rolled
     */
    async rollOtherDamage(config, dialog) {
      if (!this.otherActivity || !config.workflow) return void 0;
      if (!(this.otherActivity.hasDamage || this.otherActivity.hasHealing || this.otherActivity.roll?.formula)) {
        return void 0;
      }
      await config.workflow.buildOtherDamageMatches();
      if (this.otherCondition && !config.workflow.otherDamageMatches?.size) return void 0;
      let otherResult;
      const otherConfig = {
        ...config,
        midiOptions: {
          ...config.midiOptions,
          fastForward: config.midiOptions?.fastForwardDamage,
          updateWorkflow: false,
          // rollFormula will try and restart the workflow
          addOptionalBonuses: false
          // don't apply optional bonus flags to other activity damage
        }
      };
      if (this.otherActivity.ac && config.workflow.rollOptions?.rollToggle) {
        dialog.configure = !dialog.configure;
      }
      let otherActivity = this.otherActivity;
      const scaling = config.workflow?.chatCard?.getFlag(MODULE_ID, "use.otherScaling");
      if (scaling) {
        const item = otherActivity.item.clone({ flags: { dnd5e: { scaling } } }, { keepId: true });
        otherActivity = item.system.activities.get(otherActivity.id);
      }
      if (otherActivity?.hasDamage) {
        otherResult = await otherActivity.rollDamage(otherConfig, dialog, { create: false });
      } else if (otherActivity?.roll?.formula) {
        otherResult = await otherActivity.rollFormula(otherConfig, dialog, { create: false });
        if (otherResult) {
          if (!(otherResult instanceof Array)) otherResult = [otherResult];
          otherResult = otherResult.map(
            (roll) => (
              //@ts-expect-error
              new game.system.dice.DamageRoll(roll.formula, {}, {})
            )
          );
        }
      }
      if (otherResult && this.otherActivityAsParentType) {
        otherResult.forEach((r) => {
          const properties = new Set(r.options.properties ?? []);
          properties?.delete(this.otherActivity.actionType);
          properties?.add(this.actionType);
          r.options.properties = Array.from(properties);
        });
      }
      if (otherResult && config.midiOptions?.updateWorkflow !== false) {
        await config.workflow.setOtherDamageRolls(otherResult);
      }
      return otherResult;
    }
    configureDamageRoll(config) {
      try {
        let workflow = config.workflow;
        if (!workflow) return void 0;
        if (workflow.workflowType === "TrapWorkflow") workflow.rollOptions.fastForward = true;
        if (workflow.currentAction !== workflow.WorkflowState_WaitForDamageRoll && workflow.noAutoAttack) {
          workflow.currentAction = workflow.WorkflowState_WaitForDamageRoll;
        }
        if (workflow.currentAction !== workflow.WorkflowState_WaitForDamageRoll) {
          if (workflow.currentAction === workflow.WorkflowState_AwaitTemplate)
            return ui.notifications?.warn(i18n("midi-qol.noTemplateSeen"));
          else if (workflow.currentAction === workflow.WorkflowState_WaitForAttackRoll)
            return ui.notifications?.warn(i18n("midi-qol.noAttackRoll"));
        }
        if (workflow && (workflow.damageRollCount ?? 0) > 0) {
          workflow.displayChatCardWithoutDamageDetail();
        }
        ;
        if (debugEnabled > 0) warn("rolling damage  ", this.name, this);
        if (workflow && config.midiOptions?.isCritical !== void 0)
          workflow.isCritical = config.midiOptions?.isCritical;
        config.midiOptions ??= {};
        config.midiOptions.fastForwardDamage ??= workflow.workflowOptions?.fastForwardDamage ?? workflow.rollOptions.fastForwardDamage;
        if (workflow) workflow.damageRollCount += 1;
      } catch (err) {
        const message = "Configure Damage Roll error";
        TroubleShooter.recordError(err, message);
        error2(message, err);
      }
    }
    getDamageConfig(config = {}) {
      config.attackMode = config.workflow?.attackMode;
      const rollConfig = super.getDamageConfig(config);
      this.configureDamageRoll(rollConfig);
      this.createDamageRollModifiers(rollConfig);
      return rollConfig;
    }
    getDamageRollModifiers(roll) {
      const modifiers = [];
      const actionType = this.actionType;
      const itemType = this.item.type;
      const damageTypes = roll.options.types;
      const changes = [];
      if (this.actor) for (let effect of this.actor.allApplicableEffects()) {
        if (effect.disabled || effect.isSuppressed) continue;
        for (let change of effect.changes) {
          if (!change.key.startsWith("flags.midi-qol.rollModifiers.damage.")) continue;
          changes.push(change);
        }
      }
      changes.sort((a, b) => a.priority - b.priority);
      for (let change of changes) {
        const [_, __, ___, ____, actionOrItem, damageType] = change.key.split(".");
        if (actionOrItem === "all" || actionOrItem === actionType || actionOrItem === itemType) {
          if (damageType === "all" || damageTypes.includes(damageType)) {
            modifiers.push(change.value);
          }
        }
      }
      return modifiers;
    }
    createDamageRollModifiers(rollConfig) {
      const rolls = rollConfig.rolls;
      rolls?.forEach((roll) => {
        const modifiers = this.getDamageRollModifiers(roll);
        roll.options ??= {};
        roll.options["midi-qol"] ??= {};
        roll.options["midi-qol"].modifiers ??= [];
        roll.options["midi-qol"].modifiers = roll.options["midi-qol"].modifiers.concat(modifiers);
        if (roll.options?.properties && this.midiProperties?.magicDamage && !roll.options.properties.includes("mgc")) roll.options.properties.push("mgc");
      });
    }
    async postProcessDamageRoll(config, result) {
      const DamageRoll = CONFIG.Dice.DamageRoll;
      const rollMode = safeGetGameSetting("core", "rollMode");
      try {
        if (!config.workflow) return result;
        let magicalDamage = this.item?.system.magicAvailable;
        magicalDamage ??= this.item?.system.properties.has("mgc");
        magicalDamage = magicalDamage || configSettings.requireMagical === "off" && this.attackBonus > 0;
        magicalDamage ||= configSettings.requireMagical === "off" && (config.ammunition?.system.magicalBonus ?? 0) > 0;
        magicalDamage = magicalDamage || configSettings.requireMagical === "off" && (this.attack?.type.classification ?? "none") !== "weapon";
        magicalDamage = magicalDamage || configSettings.requireMagical === "nonspell" && this.isSpell;
        if (result?.length > 0) {
          result.forEach((roll) => {
            const droll = roll;
            if (!droll.options.properties) droll.options.properties = [];
            if (this.isSpell) droll.options.properties.push("spell");
            if (magicalDamage && !droll.options.properties.includes("mgc")) droll.options.properties.push("mgc");
            droll.options.properties.push(this.actionType);
            if (droll.data.actorType === configSettings.averageDamage || configSettings.averageDamage === "all")
              averageDice(droll);
          });
        }
        const firstTarget = config.workflow.hitTargets.first() ?? config.workflow.targets?.first();
        const firstTargetActor = firstTarget?.actor;
        const targetMaxFlags = firstTargetActor?.flags?.[MODULE_ID]?.grants?.max?.damage ?? {};
        const maxFlags = config.workflow.actor.flags?.[MODULE_ID]?.max ?? {};
        let needsMaxDamage = maxFlags.damage?.all && await evalActivationCondition2(config.workflow, maxFlags.damage.all, firstTarget, { async: true, errorReturn: false }) || maxFlags.damage && maxFlags.damage[this.actionType] && await evalActivationCondition2(config.workflow, maxFlags.damage[this.actionType], firstTarget, { async: true, errorReturn: false });
        needsMaxDamage = needsMaxDamage || (targetMaxFlags.all && await evalActivationCondition2(config.workflow, targetMaxFlags.all, firstTarget, { async: true, errorReturn: false }) || targetMaxFlags[this.actionType] && await evalActivationCondition2(config.workflow, targetMaxFlags[this.actionType], firstTarget, { async: true, errorReturn: false }));
        const targetMinFlags = firstTargetActor?.flags?.[MODULE_ID]?.grants?.min?.damage ?? {};
        const minFlags = config.workflow.actor.flags?.[MODULE_ID]?.min ?? {};
        let needsMinDamage = minFlags.damage?.all && await evalActivationCondition2(config.workflow, minFlags.damage.all, firstTarget, { async: true, errorReturn: false }) || minFlags?.damage && minFlags.damage[this.actionType] && await evalActivationCondition2(config.workflow, minFlags.damage[this.actionType], firstTarget, { async: true, errorReturn: false });
        needsMinDamage = needsMinDamage || (targetMinFlags.all && await evalActivationCondition2(config.workflow, targetMinFlags.all, firstTarget, { async: true, errorReturn: false }) || targetMinFlags[this.actionType] && await evalActivationCondition2(config.workflow, targetMinFlags[this.actionType], firstTarget, { async: true, errorReturn: false }));
        if (needsMaxDamage && needsMinDamage) {
          needsMaxDamage = false;
          needsMinDamage = false;
        }
        const bonusDamageFlags = firstTargetActor?.flags?.[MODULE_ID]?.grants?.bonus?.damage ?? {};
        if (bonusDamageFlags.all || bonusDamageFlags[this.actionType]) {
          const newDamageRoll = bonusDamageFlags[this.actionType] ?? bonusDamageFlags.all;
          const bonusDamage = await new DamageRoll(`${newDamageRoll}`, firstTargetActor?.getRollData()).roll();
          result[0] = addRollTo(result[0], bonusDamage);
        }
        let actionFlavor;
        actionFlavor = this.actionType === "heal" ? getHealingLabel() : i18n("DND5E.DamageRoll");
        const title = this.name;
        const speaker = getSpeaker(this.actor);
        let flavor = title;
        if (this.item.labels.damages?.length > 0) {
          flavor = `${title} (${this.item.labels.damages.map((d) => d.damageType)})`;
        }
        let messageData = foundry.utils.mergeObject({
          title,
          flavor,
          speaker
        }, { "flags.dnd5e.roll": { type: "damage", itemId: this.item.id, itemUuid: this.item.uuid } });
        if (needsMaxDamage) {
          for (let i = 0; i < result.length; i++) {
            result[i] = await result[i].reroll({ maximize: true });
          }
        } else if (needsMinDamage) {
          for (let i = 0; i < result.length; i++) {
            result[i] = await result[i].reroll({ minimize: true });
          }
        } else if (this.actor?.flags?.[MODULE_ID]?.damage?.["reroll-kh"] || this.actor?.flags?.[MODULE_ID]?.damage?.["reroll-kl"]) {
          let result2 = [];
          for (let i = 0; i < result.length; i++) {
            result2.push(await result[i].reroll());
          }
          if (this.actor?.flags?.[MODULE_ID]?.damage?.["reroll-kh"] && sumRolls(result2) > sumRolls(result) || this.actor?.flags?.[MODULE_ID]?.damage?.["reroll-kl"] && sumRolls(result2) < sumRolls(result)) {
            [result, result2] = [result2, result];
          }
          if (config.workflow.workflowOptions?.damageRollDSN !== false) {
            let promises = result2.map((r) => displayDSNForRoll(r, "damageRoll"));
            await Promise.all(promises);
          }
          await DamageRoll.toMessage(result2, messageData, { rollMode: game.settings.get("core", "rollMode") });
        } else if (this.actor?.flags?.[MODULE_ID]?.damage?.advantage) {
        }
        setDamageRollMinTerms(result);
        if (this.actionType === "heal" && !Object.keys(GameSystemConfig.healingTypes).includes(config.workflow.defaultDamageType ?? "")) config.workflow.defaultDamageType = "healing";
        if (config.workflow?.workflowOptions?.damageRollDSN !== false) {
          let promises = result.map((r) => displayDSNForRoll(r, "damageRoll"));
          await Promise.all(promises);
        }
        if (config.midiOptions?.addOptionalBonuses !== false) {
          result = await config.workflow.processDamageRollBonusFlags(result);
        }
        return result;
      } catch (err) {
        const message = `doDamageRoll error for item ${this?.name} ${this.uuid}`;
        TroubleShooter.recordError(err, message);
        throw err;
      }
    }
    async setupCanSeeSense(usage) {
      const workflow = usage.workflow;
      const wasSightEnabled = {};
      if (!workflow) return;
      let needPause = false;
      for (let tokenRef of workflow.targets.union(/* @__PURE__ */ new Set([workflow.token]))) {
        const target = getToken(tokenRef);
        if (!target) continue;
        wasSightEnabled[target.id] = target.document.sight.enabled;
        if (target === getToken(workflow.token) && target.vision?.active && target.vision?.los) continue;
        const needsInit = !target.document.sight.enabled && configSettings.optionalRules.invisVision || target.actor?.type === "npc" || !target.isOwner && target.document.sight.enabled || (!target.vision || !target.vision?.los);
        if (needsInit) {
          initializeVision(target);
          needPause = game.modules.get("levels-3d-preview")?.active ?? false;
        }
      }
      if (needPause) {
        await busyWait(100);
        for (let tokenRef of workflow.targets) {
          const target = getToken(tokenRef);
          if (!target || !target.vision?.los) continue;
          target.vision.add();
        }
      }
      for (let target of workflow.targets) {
        const tokenCanSense = workflow.token ? canSense(workflow.token, target, globalThis.MidiQOL.InvisibleDisadvantageVisionModes) : true;
        const targetCanSense = workflow.token ? canSense(target, workflow.token, globalThis.MidiQOL.InvisibleDisadvantageVisionModes) : false;
        if (targetCanSense) workflow.targetsCanSense.add(workflow.token);
        else workflow.targetsCanSense.delete(workflow.token);
        if (tokenCanSense) workflow.tokenCanSense.add(target);
        else workflow.tokenCanSense.delete(target);
        const tokenCanSee = workflow.token ? canSee(workflow.token, target) : true;
        const targetCanSee = workflow.token ? canSee(target, workflow.token) : false;
        if (targetCanSee) workflow.targetsCanSee.add(workflow.token);
        else workflow.targetsCanSee.delete(workflow.token);
        if (tokenCanSee) workflow.tokenCanSee.add(target);
        else workflow.tokenCanSee.delete(target);
      }
      if (workflow.token?.actor) {
        const bloodsenseRange = Number(foundry.utils.getProperty(workflow.token.actor, `flags.${MODULE_ID}.bloodsense`) ?? 0);
        if (bloodsenseRange > 0) {
          for (const target of workflow.targets) {
            if (workflow.tokenCanSense.has(target) && workflow.tokenCanSee.has(target)) continue;
            const targetToken = getToken(target);
            if (!targetToken?.actor) continue;
            const hp = targetToken.actor.system.attributes?.hp;
            if (!hp || hp.value >= hp.max) continue;
            const distance = getDistance(workflow.token, targetToken, { wallsBlock: false });
            if (distance >= 0 && distance <= bloodsenseRange) {
              workflow.tokenCanSense.add(target);
              workflow.tokenCanSee.add(target);
            }
          }
        }
      }
      workflow.targetsCanSense = workflow.targetsCanSense;
      workflow.tokenCanSense = workflow.tokenCanSense;
      workflow.targetsCanSee = workflow.targetsCanSee;
      workflow.tokenCanSee = workflow.tokenCanSee;
      for (let targetRef of workflow.targets) {
        const target = getToken(targetRef);
        if (!target) continue;
        target.document.sight.enabled = wasSightEnabled[target.id];
        target.initializeVisionSource();
      }
      if (workflow.token) {
        workflow.token.document.sight.enabled = wasSightEnabled[workflow.token.id];
        workflow.token.initializeVisionSource();
      }
    }
    async setupTargets(config, dialog, message) {
      if (!config.workflow) {
        const errMessage = "MidiQOL | MidiActivity | setupTargets | No workflow found";
        error2(errMessage);
        TroubleShooter.recordError(new Error("No workflow found"), errMessage);
        return false;
      }
      const workflow = config.workflow;
      const hasTargetsToUse = (config.midiOptions?.targetsToUse?.size ?? 0) > 0 && !activityHasAreaTarget(this);
      if (hasTargetsToUse) {
        const tokenIds = Array.from(config.midiOptions.targetsToUse).map((t) => t.id);
        updateUserTargets(tokenIds);
      }
      const isSelfTargetActivity = this.target?.affects?.type === "self";
      if (!isSelfTargetActivity && (requiresTargetConfirmation(this, config.midiOptions ?? {}) || configSettings.enforceSingleWeaponTarget)) {
        if (!await preTemplateTargets(this, config.midiOptions ?? {}))
          return false;
        workflow.setTargets(validTargetTokens(new Set(game.user?.targets)));
      } else if (hasTargetsToUse) {
        workflow.setTargets(validTargetTokens(config.midiOptions.targetsToUse));
      } else {
        let selfTarget = this.target?.affects.type === "self";
        if (this.item.type === "tool" && !this.target?.affects.type)
          selfTarget = true;
        if (!selfTarget) {
          workflow.setTargets(validTargetTokens(new Set(game.user?.targets)));
        } else {
          foundry.utils.setProperty(config, "midiOptions.workflowOptions.targetConfirmation", "none");
          let potentialTarget = getOrCreateTokenForActor(this.actor);
          workflow.setTargets(/* @__PURE__ */ new Set());
          if (potentialTarget) workflow.setTargets(workflow.targets.add(potentialTarget));
        }
      }
      if (canvas.scene) {
        const tokensIdsToUse = Array.from(workflow.targets).filter((t) => isValidTarget(t)).map((t) => t.id);
        updateUserTargets(tokensIdsToUse);
      }
      return true;
    }
    async confirmTargets() {
    }
    async preTargetingChecks(usage, dialog, message, optionsConfig = {}) {
      const workflow = usage.workflow;
      const removeWorkflow = /* @__PURE__ */ __name((workflow2) => {
        return false;
      }, "removeWorkflow");
      if (!workflow) return false;
      let options = { none: false, checkAoO: true, checkReaction: true, checkBonusAction: true, checkAllowIncapacitated: false, callMacros: true, callHooks: true, checkTargets: true, checkComponents: true, checkUse: true };
      if (optionsConfig.none) options = optionsConfig;
      options = foundry.utils.mergeObject(options, optionsConfig, { insertKeys: true, overwrite: true });
      if (usage.midiOptions?.proceedChecks?.none) options = usage.midiOptions.proceedChecks;
      options = foundry.utils.mergeObject(options, usage.midiOptions?.proceedChecks ?? {}, { insertKeys: true, overwrite: true });
      let cancelWorkflow2;
      if (options.checkAllowIncapacitated && !usage.midiOptions?.workflowOptions?.allowIncapacitated && checkMechanic("incapacitated") !== "nothing") {
        const condition = checkIncapacitated(this.actor, true, false);
        if (condition) {
          logIncapacitatedCheckResult(this.actor?.name ?? "", condition, debugEnabled > 0, true);
          if (checkMechanic("incapacitated") === "enforce") return removeWorkflow(usage.workflow);
        }
      }
      if (options.callHooks) {
        cancelWorkflow2 = await callActivityHooks(this, "midi-qol.preTargeting", { activity: this, token: workflow.token, config: usage, dialog, message }) === false;
        cancelWorkflow2 ||= await callActivityHooks(this, "midi-qol.preTargetingV2", { workflow, usage, dialog, message }) === false;
      }
      if (options.callMacros && configSettings.allowUseMacro) {
        const results = await workflow?.callMacros(this.item, workflow?.onUseMacros?.getMacros("preTargeting"), "OnUse", "preTargeting", { usage, dialog, message });
        cancelWorkflow2 ||= results?.some((i) => i === false) ?? false;
      }
      if (cancelWorkflow2) return removeWorkflow(usage.workflow);
      return true;
    }
    async preChatChecks(usage, dialog, message, optionsConfig = {}) {
      const workflow = usage.workflow;
      const removeWorkflow = /* @__PURE__ */ __name((workflow2) => {
        return false;
      }, "removeWorkflow");
      if (!workflow) return false;
      let options = { none: false, checkAoO: true, checkReaction: true, checkBonusAction: true, checkAllowIncapacitated: false, callMacros: true, callHooks: true, checkTargets: true, checkComponents: true, checkUse: true };
      if (optionsConfig.none) options = optionsConfig;
      else
        options = foundry.utils.mergeObject(options, optionsConfig, { insertKeys: true, overwrite: true });
      if (usage.midiOptions?.proceedChecks?.none) options = usage.midiOptions.proceedChecks;
      else
        options = foundry.utils.mergeObject(options, usage.midiOptions?.proceedChecks ?? {}, { insertKeys: true, overwrite: true });
      if (options.callHooks) {
        let hookAbort = await callActivityHooks(this, "midi-qol.preItemRoll", { activity: this, token: workflow.token, config: usage, dialog, message }) === false;
        hookAbort ||= await callActivityHooks(this, "midi-qol.preItemRollV2", { workflow, usage, dialog, message }) === false;
        if (hookAbort || workflow.aborted) {
          console.warn("midi-qol | attack roll blocked by preItemRoll hook");
          workflow.aborted = true;
          await workflow.performState(workflow.WorkflowState_Abort);
          return removeWorkflow(workflow);
        }
      }
      if (options.callMacros && configSettings.allowUseMacro) {
        const results = await workflow.callMacros(workflow.item, workflow.onUseMacros?.getMacros("preItemRoll"), "OnUse", "preItemRoll", { usage, dialog, message });
        if (workflow.aborted || results.some((i) => i === false)) {
          console.warn("midi-qol | item roll blocked by preItemRoll macro");
          workflow.aborted = true;
          await workflow.performState(workflow.WorkflowState_Abort);
          return removeWorkflow(workflow);
        }
      }
      if (options.checkUse && this.useCondition && this.effectiveActivationType !== "reaction") {
        if (!await evalActivationCondition2(workflow, this.useCondition, workflow.targets.first(), { async: true })) {
          const message2 = `${this.useConditionReason} ${this.actor?.name} unable to use ${this.item.name}:${this.name}`;
          if (!(usage.midiOptions?.noUseWarning || usage.midiOptions?.workflowOptions?.noUseWarning))
            ui.notifications?.warn(message2);
          else
            console.warn(`midi-qol | preChatChecks | ${message2} `);
          return removeWorkflow(workflow);
        }
      }
      if (this.isSpell || this.item?.system.type?.value === "scroll") {
        const midiFlags2 = this.actor?.flags[MODULE_ID];
        const needsVerbal = this.item.system.properties.has("vocal");
        const needsSomatic = this.item.system.properties.has("somatic");
        const needsMaterial = this.item.system.properties.has("material");
        const conditionData = createConditionData({ actor: this.actor, activity: this });
        const notSpell = await evalCondition(midiFlags2?.fail?.spell?.all, conditionData, { errorReturn: false, async: true });
        if (notSpell) {
          ui.notifications?.warn("You are unable to cast the spell");
          return removeWorkflow(usage.workflow);
        }
        if (options.checkComponents) {
          let notVerbal = await evalCondition(midiFlags2?.fail?.spell?.verbal, conditionData, { errorReturn: false, async: true });
          if (notVerbal && needsVerbal) {
            ui.notifications?.warn("You make no sound and the spell fails");
            return removeWorkflow(usage.workflow);
          }
          notVerbal = notVerbal || await evalCondition(midiFlags2?.fail?.spell?.vocal, conditionData, { errorReturn: false, async: true });
          if (notVerbal && needsVerbal) {
            ui.notifications?.warn("You make no sound and the spell fails");
            return removeWorkflow(usage.workflow);
          }
          const notSomatic = await evalCondition(midiFlags2?.fail?.spell?.somatic, conditionData, { errorReturn: false, async: true });
          if (notSomatic && needsSomatic) {
            ui.notifications?.warn("You can't make the gestures and the spell fails");
            return removeWorkflow(usage.workflow);
          }
          const notMaterial = await evalCondition(midiFlags2?.fail?.spell?.material, conditionData, { errorReturn: false, async: true });
          if (notMaterial && needsMaterial) {
            ui.notifications?.warn("You can't use the material component and the spell fails");
            return removeWorkflow(usage.workflow);
          }
        }
      }
      workflow.AoO = false;
      workflow.itemUsesReaction = false;
      if (!usage.midiOptions?.workflowOptions?.notReaction && options.checkReaction && this.actor?.inCombat) {
        const isTurn = game.combat?.combatant === game.combat?.getCombatantsByActor(this.actor)[0];
        if (["reaction", "reactiondamage", "reactionmanual", "reactionpreattack"].includes(this.effectiveActivationType ?? "") && (this.activation?.cost ?? 1) > 0) {
          workflow.itemUsesReaction = true;
        }
        if (!isTurn && needsAOOCheck(this.actor) && this.attack && this.effectiveActivationType !== "special") {
          workflow.itemUsesReaction = true;
          workflow.AoO = true;
        }
        const queryReaction = workflow.itemUsesReaction && hasUsedReaction(this.actor) && this.actor.inCombat && needsReactionCheck(this.actor);
        if (queryReaction) {
          const shouldRoll = await foundry.applications.api.DialogV2.confirm({
            window: {
              title: "midi-qol.EnforceReactions.Title"
            },
            content: i18n("midi-qol.EnforceReactions.Content")
          });
          if (!shouldRoll) {
            await workflow.performState(workflow.WorkflowState_Abort);
            return removeWorkflow(usage.workflow);
          }
        }
      }
      workflow.itemUsesBonusAction = false;
      if (!usage.midiOptions?.workflowOptions?.notBonusAction && options.checkBonusAction && this.actor?.inCombat && needsBonusActionCheck(this.actor)) {
        workflow.itemUsesBonusAction = ["bonus"].includes(this.effectiveActivationType ?? "");
        const queryBonus = workflow.itemUsesBonusAction && hasUsedBonusAction(this.actor) && needsBonusActionCheck(this.actor);
        if (queryBonus) {
          const shouldRoll = await foundry.applications.api.DialogV2.confirm({
            window: {
              title: "midi-qol.EnforceBonusActions.Title"
            },
            content: i18n("midi-qol.EnforceBonusActions.Content")
          });
          if (!shouldRoll) {
            await workflow.performState(workflow.WorkflowState_Abort);
            return removeWorkflow(usage.workflow);
          }
        }
      }
      return true;
    }
    async postChatCardChecks(usage, dialog, message, optionsConfig = {}) {
      const workflow = usage.workflow;
      const removeWorkflow = /* @__PURE__ */ __name(async (workflow2) => {
        Workflow.removeWorkflow(workflow2.id);
        const consumed = getConsumptionData(workflow2.chatCard);
        if (!foundry.utils.isEmpty(consumed)) {
          await workflow2.activity.refund(consumed);
        }
        await workflow2.chatCard?.delete();
        return false;
      }, "removeWorkflow");
      if (debugEnabled > 0)
        warn("MidiQOL | postChatCardChecks | Called", this);
      if (!workflow) return false;
      let options = { none: false, checkAoO: true, checkReaction: true, checkBonusAction: true, checkAllowIncapacitated: false, callMacros: true, callHooks: true, checkTargets: true, checkComponents: true, checkUse: true };
      if (optionsConfig.none) options = optionsConfig;
      else
        options = foundry.utils.mergeObject(options, optionsConfig, { insertKeys: true, overwrite: true });
      if (usage.midiOptions?.proceedChecks?.none) options = usage.midiOptions.proceedChecks;
      else
        options = foundry.utils.mergeObject(options, usage.midiOptions?.proceedChecks ?? {}, { insertKeys: true, overwrite: true });
      try {
        let isEmanationTargeting = activityHasAutoPlaceTemplate(this) || activityHasEmanationNoTemplate(this);
        let isAoETargeting = !isEmanationTargeting && activityHasAreaTarget(this);
        let selfTarget = this.target?.affects.type === "self";
        const inCombat = this.actor && isInCombat(this.actor);
        const requiresTargets = configSettings.requiresTargets === "always" || configSettings.requiresTargets === "tokens" && (canvas?.scene?.tokens.size ?? 0) > 0 || configSettings.requiresTargets === "combat" && inCombat;
        let speaker = getSpeaker(this.actor);
        const token = tokenForActor(this.actor);
        let cancelWorkflow2 = false;
        let shouldAllowRoll = !options.checkTargets || !requiresTargets || workflow.targets.size > 0 || (this.target?.affects.type ?? "") === "" || selfTarget || isAoETargeting || isEmanationTargeting || !this.attack && !this.hasDamage && !this.hasSave;
        if (!shouldAllowRoll) {
          ui.notifications?.warn(i18n("midi-qol.noTargets"));
          return removeWorkflow(workflow);
        }
        let allowedTargets;
        const hasExplicitCount = this.target?.affects?.count && this.target.affects.count !== "";
        if (hasExplicitCount && !activityHasScaledTargetCount(this)) {
          allowedTargets = Number(this.target.affects.count);
        } else if (options.checkTargets && requiresTargets && shouldEnforceSingleTarget(this)) {
          allowedTargets = 1;
        } else {
          allowedTargets = Infinity;
        }
        const creatureTargetTypes2 = ["creature", "enemy", "ally", "willing"];
        if (options.checkTargets && requiresTargets && !isEmanationTargeting && !isAoETargeting && creatureTargetTypes2.includes(this.target?.affects?.type) && workflow.targets.size === 0) {
          ui.notifications?.warn(i18n("midi-qol.noTargets"));
          if (debugEnabled > 0) warn(`${game.user?.name} attempted to roll with no targets selected`);
          return removeWorkflow(workflow);
        }
        if (options.checkTargets && requiresTargets && workflow.targets.size > allowedTargets) {
          ui.notifications?.warn(i18nFormat2("midi-qol.wrongNumberTargets", { allowedTargets }));
          if (debugEnabled > 0) warn(`${game.user?.name} ${i18nFormat2(`midi-qol.${MODULE_ID}.wrongNumberTargets`, { allowedTargets })}`);
          return removeWorkflow(workflow);
        }
        let tokenToUse;
        if (speaker.token) tokenToUse = canvas.tokens?.get(speaker.token);
        const rangeDetails = checkActivityRange(this, tokenToUse, workflow.targets, checkMechanic("checkRange") !== "none" && !isAoETargeting);
        if (checkMechanic("checkRange") !== "none" && !isAoETargeting && !isEmanationTargeting && !workflow.AoO && speaker.token) {
          if (tokenToUse && workflow.targets.size > 0) {
            if (rangeDetails.result === "fail")
              return removeWorkflow(workflow);
            else {
              tokenToUse = rangeDetails.attackingToken;
            }
          }
        }
        if (!workflow) return false;
        workflow.token = tokenToUse;
        workflow.rangeDetails = rangeDetails;
        if (configSettings.undoWorkflow) await saveUndoData(workflow);
        const ammoConsumption = this.consumption?.targets?.find((t) => t.type === "ammo");
        if (ammoConsumption?.target) {
          workflow.ammunition = this.actor?.items.get(ammoConsumption.target);
        }
      } catch (err) {
        const message2 = `postChatCardChecks error for ${this.actor?.name} ${this.name} ${this.uuid}`;
        TroubleShooter.recordError(err, message2);
        throw err;
      }
      return true;
    }
    _usageChatButtons(message) {
      let buttons = super._usageChatButtons(message);
      const autoCreateTemplate = activityHasAutoPlaceTemplate(this);
      const emanationNoTemplate = activityHasEmanationNoTemplate(this);
      if (autoCreateTemplate || emanationNoTemplate) {
        buttons = buttons.filter((b) => b.dataset?.action !== "placeTemplate");
      }
      if (this.otherActivity?.hasConsumption()) {
        buttons.push({
          label: i18n("midi-qol.ConsumeOtherActivityResource"),
          icon: '<i class="fa-solid fa-cubes-stacked" inert></i>',
          dataset: { action: "consumeOtherActivity" }
        }, {
          label: i18n("midi-qol.RefundOtherActivityResource"),
          icon: '<i class="fa-solid fa-clock-rotate-left" inert></i>',
          dataset: { action: "refundOtherActivity" }
        });
      }
      return buttons;
    }
    /**
     * Determine whether a chat button should be hidden based on current state.
     * Mirrors dnd5e's shouldHideChatButton pattern.
     */
    shouldHideChatButton(button, message) {
      const consumed = message.getFlag(MODULE_ID, "use.consumed");
      switch (button.dataset.action) {
        // Show consume button only when rolled but didn't consume (empty object {})
        // Hide if: cancelled (false), not checked (undefined), or already consumed (non-empty object)
        case "consumeOtherActivity":
          return !(consumed && foundry.utils.isEmpty(consumed));
        // Show refund button only when actually consumed (non-empty object)
        case "refundOtherActivity":
          return !consumed || foundry.utils.isEmpty(consumed);
        // dnd5e hides placeTemplate when !game.canvas.scene, which is true during page reload
        // before canvas is ready — only check permission here
        case "placeTemplate":
          return !game.user?.can("TEMPLATE_CREATE");
      }
      return super.shouldHideChatButton?.(button, message) ?? false;
    }
    async _placeEmanationTemplate(workflow) {
      const autoCreateTemplate = activityHasAutoPlaceTemplate(this);
      const actor = this.item.parent;
      const tokenToUse = workflow.token ?? getToken(actor);
      if (!tokenToUse) return;
      if (autoCreateTemplate) {
        const gs = canvas.dimensions?.distance ?? 5;
        const templateOptions = {};
        let item = this;
        let target = this.target ?? { value: 0, template: { size: 0 } };
        const fudge = 0.1;
        const { width, height } = tokenToUse.document;
        templateOptions.distance = Math.ceil(Number(target.template.size) + Math.max((width ?? 1) / 2, (height ?? 1) / 2, 0) * (canvas.dimensions?.distance ?? 0));
        templateOptions.x = tokenToUse.center?.x ?? 0;
        templateOptions.y = tokenToUse.center?.y ?? 0;
        foundry.utils.setProperty(templateOptions, `flags.${MODULE_ID}.actorUuid`, actor?.uuid);
        foundry.utils.setProperty(templateOptions, `flags.${MODULE_ID}.tokenId`, tokenToUse.id);
        foundry.utils.setProperty(templateOptions, `flags.${MODULE_ID}.workflowId`, this.uuid);
        foundry.utils.setProperty(templateOptions, `flags.${MODULE_ID}.itemUuid`, this.item.uuid);
        let templates = game.system.canvas.AbilityTemplate.fromActivity(this, templateOptions);
        if (!templates) error2("No templates returned from fromActivity");
        let template = templates[0];
        const templateData = template.document.toObject();
        if (this.item) foundry.utils.setProperty(templateData, `flags.${MODULE_ID}.itemUuid`, this.item.uuid);
        if (this.actor) foundry.utils.setProperty(templateData, `flags.${MODULE_ID}.actorUuid`, this.actor.uuid);
        if (!foundry.utils.getProperty(templateData, `flags.dnd5e.origin`)) foundry.utils.setProperty(templateData, `flags.${game.system?.id}.origin`, this.item?.uuid);
        const templateDocuments = await canvas.scene?.createEmbeddedDocuments("MeasuredTemplate", [templateData]);
        if (templateDocuments && templateDocuments.length > 0) {
          let td = templateDocuments[0];
          td.object?.refresh();
          await busyWait(10);
          if (workflow) {
            workflow.templateUuid = td.uuid;
            if (installedModules.get("walledtemplates") && this.flags?.walledtemplates?.attachToken === "caster") {
              await tokenToUse.attachTemplate(td.object, { "flags.dae.stackable": "noneName" }, true);
              if (workflow && !foundry.utils.getProperty(this, "item.flags.walledtemplates.noAutotarget"))
                selectTargetsFromTemplates([td]);
            } else if (getActivityAutoTargetAction2(this) !== "none") selectTargetsFromTemplates([td]);
          } else selectTargetsFromTemplates([td]);
          return templateDocuments;
        }
      }
    }
    async _usageChatContext(message) {
      const workflow = message.workflow;
      let systemCard = message.systemCard ?? false;
      const minimalCard = false;
      const tokenToUse = getToken(this.item.parent);
      const autoCreateTemplate = activityHasAutoPlaceTemplate(this);
      const emanationNoTemplate = activityHasEmanationNoTemplate(this);
      if (tokenToUse && autoCreateTemplate) {
        this.templates = await this._placeEmanationTemplate(workflow);
      }
      if (tokenToUse && emanationNoTemplate) {
        setRangedTargets(tokenToUse, this.target);
        message.workflow?.setTargets(validTargetTokens(new Set(game.user?.targets)));
        foundry.utils.setProperty(message.data, "flags.dnd5e.targets", globalThis.dnd5e.utils.getTargetDescriptors());
      }
      const context = await super._usageChatContext(message);
      if (debugEnabled > 0) warn("show item card ", this, this.actor, this.actor?.token, systemCard, workflow);
      let needAttackButton = !getRemoveAttackButtons(this) || configSettings.mergeCardMulti || configSettings.confirmAttackDamage !== "none" || !(workflow?.someAutoRollEventKeySet() && !getAutoRollAttack(workflow));
      const needDamageButton = (this.hasDamage || this.hasHealing) && (["none", "saveOnly"].includes(getAutoRollDamage(workflow)) || workflow?.rollOptions.rollToggle || configSettings.confirmAttackDamage !== "none" || !getRemoveDamageButtons(this) || systemCard || configSettings.mergeCardMulti);
      const isPlayerOwned = this.item.actor?.hasPlayerOwner;
      const hideItemDetails = ["none", "cardOnly"].includes(configSettings.showItemDetails) || configSettings.showItemDetails === "pc" && !isPlayerOwned || !configSettings.itemTypeList?.includes(this.item.type);
      let hasEffects = workflow?.workflowType === "BaseWorkflow" && this.effects.some((ae) => !ae.effect?.transfer && !ae.effect?.flags?.dae?.dontApply);
      ;
      if (this.target?.affects.type !== "" || workflow?.targets.size !== 0)
        hasEffects &&= !["applyNoButton", "applyRemove"].includes(configSettings.autoItemEffects);
      let dmgBtnText = this.actionType === "heal" ? getHealingLabel() : i18n("DND5E.Damage");
      if (workflow?.workflowOptions?.fastForwardDamage && configSettings.showFastForward) dmgBtnText += ` ${i18n("midi-qol.fastForward")}`;
      let midiContextData = {
        hasButtons: true,
        labels: this.labels,
        //@ ts-expect-error TODO needed for abilities translation
        // config: game.system.config,
        condensed: configSettings.mergeCardCondensed && !!this.attack,
        hasAttack: this.attack && !minimalCard && (systemCard || needAttackButton || configSettings.confirmAttackDamage !== "none"),
        // @ts-expect-error no dnd5e-types
        isHealing: !minimalCard && this.item.isHealing && (systemCard || configSettings.autoRollDamage !== "always"),
        hasDamage: needDamageButton,
        hasAttackRoll: !minimalCard && this.attack,
        configSettings,
        hideItemDetails,
        dmgBtnText,
        hasEffects,
        effects: this.item.effects,
        isMerge: true,
        mergeCardMulti: configSettings.mergeCardMulti && (this.attack || this.hasDamage),
        confirmAttackDamage: configSettings.confirmAttackDamage !== "none" && (this.attack || this.hasDamage),
        RequiredMaterials: i18n("DND5E.RequiredMaterials"),
        Attack: i18n("DND5E.Attack"),
        OtherFormula: i18n("DND5E.OtherFormula"),
        displayProperties: configSettings.displayProperties,
        canCancel: configSettings.undoWorkflow
        // TODO enable this when more testing done.
      };
      if (!configSettings.displayProperties && context.supplements) {
        const materialsLabel = i18n("DND5E.Materials");
        context.supplements = context.supplements.filter((s) => !s.includes(`>${materialsLabel}</`));
      }
      const rollSaveOptions = /* @__PURE__ */ new Set();
      rollSaveOptions.add(configSettings.rollNPCLinkedSaves);
      rollSaveOptions.add(configSettings.rollNPCSaves);
      rollSaveOptions.add(configSettings.playerRollSaves);
      const showSaveButtons = rollSaveOptions.has("none") || rollSaveOptions.has("chat") || configSettings.autoCheckSaves === "none";
      if (this.otherActivity?.save && !this.save && showSaveButtons) {
        context.buttons = context.buttons.concat(this.otherActivity._usageChatButtons(message));
      } else if (this.otherActivity?.check && !this.check && showSaveButtons) {
        context.buttons = context.buttons.concat(this.otherActivity._usageChatButtons(message));
      }
      context.buttons = context.buttons?.filter((b) => !["rollAttack", "rollDamage", "rollHealing"].includes(b.dataset?.action));
      if (!showSaveButtons) context.buttons = context.buttons?.filter((b) => !["rollSave", "rollCheck"].includes(b.dataset?.action));
      if (configSettings.autoCheckSaves !== "none") context.buttons?.forEach((b) => {
        b.isDisabled = ["rollSave", "rollCheck"].includes(b.dataset?.action) ? "disabled" : "";
      });
      return foundry.utils.mergeObject(context, midiContextData);
    }
    get actionType() {
      return this.metadata.type;
    }
    get otherActivity() {
      if (this._otherActivity !== void 0) return this._otherActivity;
      if (this.otherActivityId === "none" || this.otherActivityId === void 0) return void 0;
      if (this.ammunitionItem) {
        this._otherActivity = this.ammunitionItem.system.activities?.find(
          (a) => a.midiProperties?.automationOnly && a.isOtherActivityCompatible
        );
        if (!this._otherActivity) {
          this._otherActivity = this.ammunitionItem.system.activities.find(
            (a) => a.isOtherActivityCompatible
          );
        }
        if (this._otherActivity) {
          this._otherActivity.prepareData();
          return this._otherActivity;
        }
      }
      if (this.otherActivityId !== "") {
        this._otherActivity = this.item.system.activities.get(this.otherActivityId);
        if (!this._otherActivity) this._otherActivity = this.item.system.activities.find((a) => a.identifier === this.otherActivityId);
        this._otherActivity?.prepareData();
        return this._otherActivity;
      }
      if (!this.attack) {
        this._otherActivity = null;
        return this._otherActivity;
      }
      if (!this._otherActivity) {
        const otherActivityOptions = this.item.system.activities.filter((a) => a.midiProperties?.automationOnly && a.isOtherActivityCompatible && a.uuid !== this.uuid);
        if (otherActivityOptions.length === 1) {
          this._otherActivity = otherActivityOptions[0];
        }
      }
      if (!this._otherActivity) {
        const otherActivityOptions = this.item.system.activities.filter((a) => a.isOtherActivityCompatible && a.uuid !== this.uuid);
        if (otherActivityOptions.length === 1) {
          this._otherActivity = otherActivityOptions[0];
        }
      }
      this._otherActivity?.prepareData();
      if (!this._otherActivity) this._otherActivity = null;
      return this._otherActivity;
    }
    get useCondition() {
      return this.useConditionText ?? "";
    }
    get effectCondition() {
      return this.effectConditionText ?? "";
    }
    get reactionCondition() {
      return this.useCondition ?? "";
    }
    get otherCondition() {
      return this.otherActivity?.useCondition ?? "";
    }
    get hasDamage() {
      return (this.damage?.parts?.length ?? 0) > 0;
    }
    get hasHealing() {
      return this.healing !== void 0;
    }
    get hasAttack() {
      return this.attack !== void 0;
    }
    get hasSave() {
      return !!this.save || !!this.check;
    }
  }
  return MidiActivityMixin2;
}, "MidiActivityMixin");
var MidiActivityMixinSheet = /* @__PURE__ */ __name((Base) => {
  return class MidiActivitySheet extends Base {
    static {
      __name(this, "MidiActivitySheet");
    }
    static DEFAULT_OPTIONS = {
      ...super.DEFAULT_OPTIONS,
      actions: {
        ...super.DEFAULT_OPTIONS.actions,
        addMacro: MidiActivitySheet.#addMacro,
        openConditionEditor: MidiActivitySheet.#openConditionEditor
      }
    };
    static #addMacro() {
      const Editor = globalThis.DAE?.DIMEditor;
      if (Editor) new Editor({ document: this.activity }).render({ force: true });
      else ui.notifications?.error("DIMEditor not available - install Dynamic Active Effects");
    }
    static #openConditionEditor(event, target) {
      const fieldName = target.dataset.fieldName ?? "";
      const fieldLabel = target.dataset.fieldLabel ?? fieldName;
      const input = target.closest(".midi-condition-field-wrapper")?.querySelector("input");
      if (!input) return;
      new ConditionEditor({
        value: input.value,
        key: fieldName,
        label: fieldLabel,
        parent: this.activity?.item ?? null,
        onSubmit: /* @__PURE__ */ __name((newValue) => {
          input.value = newValue;
          input.dispatchEvent(new Event("change", { bubbles: true }));
        }, "onSubmit"),
        onCancel: /* @__PURE__ */ __name(() => {
        }, "onCancel")
      }).render({ force: true });
    }
    static PARTS = {
      ...super.PARTS,
      "midi-qol": {
        template: "modules/midi-qol/templates/activity/parts/midi-activity-tab.hbs"
      }
    };
    _getTabs() {
      const midiSubTabs = ["modConditions", "workflowSettings", "activityManagement", "overtime", "activityMacro"];
      if (!midiSubTabs.includes(this.tabGroups["midi-qol"])) {
        this.tabGroups["midi-qol"] = "modConditions";
      }
      let tabs = super._getTabs();
      tabs = {
        ...tabs,
        "midi-qol": {
          id: "midi-qol",
          group: "sheet",
          icon: "fa-solid fa-sun",
          label: "midi-qol",
          tabs: {
            modConditions: {
              id: "modConditions",
              group: "midi-qol",
              icon: "fa-solid fa-filter",
              label: i18n("midi-qol.ActivitySheetTabs.modConditions")
            },
            workflowSettings: {
              id: "workflowSettings",
              group: "midi-qol",
              icon: "fa-solid fa-gears",
              label: i18n("midi-qol.ActivitySheetTabs.workflowSettings")
            },
            activityManagement: {
              id: "activityManagement",
              group: "midi-qol",
              icon: "fa-solid fa-sitemap",
              label: i18n("midi-qol.ActivitySheetTabs.activityManagement")
            },
            overtime: {
              id: "overtime",
              group: "midi-qol",
              icon: "fa-solid fa-clock",
              label: i18n("midi-qol.ActivitySheetTabs.overtime")
            },
            activityMacro: {
              id: "activityMacro",
              group: "midi-qol",
              icon: "fa-solid fa-file-pen",
              label: i18n("midi-qol.ActivitySheetTabs.activityMacro")
            }
          }
        }
      };
      return super._markTabs(tabs);
    }
    async _prepareMidiQolContext(context) {
      context.ConfirmTargetOptions = Object.entries(confirmTargetOptions).map(([value, label]) => ({ value, label }));
      if (this.activity.forcedTargetConfirmation) context.ConfirmTargetOptions = context.ConfirmTargetOptions.filter((o) => o.value === this.activity.forcedTargetConfirmation);
      const ROLL_MODES = CONST.DICE_ROLL_MODES;
      context.RollModeOptions = [
        { value: "default", label: i18n("Default") },
        { value: ROLL_MODES.PUBLIC, label: i18n("CHAT.RollPublic") },
        { value: ROLL_MODES.PRIVATE, label: i18n("CHAT.RollPrivate") },
        { value: ROLL_MODES.BLIND, label: i18n("CHAT.RollBlind") },
        { value: ROLL_MODES.SELF, label: i18n("CHAT.RollSelf") }
      ];
      context.RemoveChatButtonsOptions = Object.entries(foundry.utils.mergeObject({ "default": i18n("midi-qol.MidiSettings") }, removeButtonsOptions)).map(([value, label]) => ({ value, label }));
      context.placeholderIdentifier = context.activity.identifier;
      const midiProperties = context.activity.midiProperties;
      context.ConsumeDialogOptions = Object.entries(consumeConfigOptions).map(([value, label]) => {
        return { value, label, selected: midiProperties.forceConsumeDialog === value };
      });
      ;
      context.RollDialogOptions = Object.entries(rollConfigOptions).map(([value, label]) => {
        return { value, label, selected: midiProperties.forceRollDialog === value };
      });
      context.DamageDialogOptions = Object.entries(damageConfigOptions).map(([value, label]) => {
        return { value, label, selected: midiProperties.forceDamageDialog === value };
      });
      context.otherActivityOptions = this.item.system.activities.filter((a) => a.id !== this.activity.id && a.isOtherActivityCompatible).reduce((ret, a) => {
        const identifierString = a.identifier === a.name.slugify() ? "" : ` (${a.identifier})`;
        ret.push({ label: `${a.name}${identifierString}`, value: a.identifier, id: a.id });
        return ret;
      }, [{ label: "Auto", value: "" }, { label: "None", value: "none" }]);
      context.otherActivityOptions?.forEach((option) => {
        option.selected = option.value === this.activity.otherActivityId || option.id === this.activity.otherActivityId;
      });
      context.triggeredActivityOptions = this.item.system.activities.filter((a) => a.id !== this.activity.id && a.isTriggerableActivity).reduce((ret, a) => {
        const identifierString = a.identifier === a.name.slugify() ? "" : ` (${a.identifier})`;
        ret.push({ label: `${a.name}${identifierString}`, value: a.identifier, id: a.id });
        return ret;
      }, [{ label: "None", value: "none" }]);
      context.triggeredActivityOptions?.filter((option) => {
        option.selected = option.value === this.activity.midiProperties.triggeredActivityId || option.id === this.activity.midiProperties.triggeredActivityId;
      });
      let activity = context.activity;
      let triggeredActivity = await context.activity.getTriggeredActivity();
      const triggerList = [activity.name];
      while (triggeredActivity) {
        triggerList.push(triggeredActivity.name);
        if (triggeredActivity.id === activity.id) {
          ui.notifications?.error(`midi-qol | Circular activity call detected ${triggerList.join("->")}`);
          break;
        }
        triggeredActivity = await triggeredActivity.getTriggeredActivity();
      }
      context.triggeredActivityTargetOptions = Object.entries(triggeredActivityTargetOptions).map(([value, label]) => {
        return { value, label, selected: midiProperties.triggeredActivityTargets === value };
      });
      context.triggeredActivityRollAsOptions = Object.entries(triggeredActivityRollAsOptions).map(([value, label]) => {
        return { value, label, selected: midiProperties.triggeredActivityRollAs === value };
      });
      if ((await context.activity.getTriggeredActivity())?.isSelfTriggerableOnly) {
        context.triggeredActivityRollAsOptions = context.triggeredActivityRollAsOptions.filter((o) => o.value === "self");
      }
      context.ignoreTraitsOptions = getIgnoreTraitsOptions().map((option) => {
        return { ...option, selected: this.activity.midiProperties.ignoreTraits.has(option.value) };
      });
      context.AutoTargetTypeOptions = Object.entries(aoeTargetTypeOptions).map(([value, label]) => ({ value, label }));
      const AutoCEEffectsOptions = Object.entries(autoCEEffectsOptions).map(([value, opt]) => ({ value, label: typeof opt === "string" ? opt : opt.label }));
      context.AutoCEEffectsOptions = [{ value: "default", label: i18n("midi-qol.MidiSettings") }, ...AutoCEEffectsOptions];
      const defaultAction = { "default": i18n("midi-qol.MidiSettings") };
      context.AutoTargetActionOptions = Object.entries(foundry.utils.mergeObject(defaultAction, autoTargetOptions)).map(([value, opt]) => ({ value, label: typeof opt === "string" ? opt : opt.label }));
      context.hasAreaTarget = this.activity.target?.template?.type;
      context.possibleOtherActivity = this.activity.possibleOtherActivity;
      context.turnChoiceOptions = [{ value: "start", label: i18n("midi-qol.OVERTIME.FIELDS.turnChoice.start") }, { value: "end", label: i18n("midi-qol.OVERTIME.FIELDS.turnChoice.end") }];
      context.overTimeRollAsOptions = [{ value: "target", label: i18n("midi-qol.OVERTIME.FIELDS.rollAs.target") }, { value: "source", label: i18n("midi-qol.OVERTIME.FIELDS.rollAs.source") }];
      context.CEActive = installedModules.get("dfreds-convenient-effects");
      return context;
    }
    async _preparePartContext(partId, context, options) {
      context = await super._preparePartContext(partId, context, options);
      context.hasDamage = this.activity.damage;
      context.isSummons = this.activity.type === "summon";
      context.consumesHP = this.activity.consumption?.targets?.some((t) => t.target === "attributes.hp.value");
      if (partId === "midi-qol") {
        context.tab = context.tabs["midi-qol"];
        return this._prepareMidiQolContext(context);
      }
      return context;
    }
  };
}, "MidiActivityMixinSheet");
function renderActivitySheetHook(app, [elem]) {
  const Editor = globalThis.DAE?.DIMEditor;
  if (!Editor) return;
  let activity = app.activity;
  if (!activity.macro) return;
  let existingButton = elem.closest(".window-header").querySelector("button.dae-dimeditor");
  if (existingButton) {
    if (activity.macro?.command) existingButton.style.color = "#36ba36";
    return;
  }
  let closeButton = elem.closest(".window-header").querySelector('button[data-action="close"]');
  let daeButton = document.createElement("button");
  const DIMtitle = i18n("dae.DIMEditor.Name");
  daeButton.setAttribute("class", "header-control fa-solid fa-file-pen dae-dimeditor icon");
  daeButton.onclick = function(ev) {
    new Editor({ document: this.activity }).render({ force: true });
  }.bind(app);
  if (activity.macro?.command) daeButton.style.color = "#36ba36";
  daeButton.title = "Activity Macro Editor";
  closeButton.parentNode.insertBefore(daeButton, closeButton);
}
__name(renderActivitySheetHook, "renderActivitySheetHook");
var MidiActivityUsageDialog;
function setupMidiActivityMixin() {
  const ActivityUsageDialog = game.system.applications.activity.ActivityUsageDialog;
  MidiActivityUsageDialog = class MidiActivityUsageDialog extends ActivityUsageDialog {
    static {
      __name(this, "MidiActivityUsageDialog");
    }
    async _prepareCreationContext(context, options) {
      context = await super._prepareCreationContext(context, options);
      if (activityHasAutoPlaceTemplate(this.activity) || activityHasEmanationNoTemplate(this.activity)) {
        context.hasCreation = false;
      }
      return context;
    }
  };
  Hooks.on("renderActivitySheet", renderActivitySheetHook);
}
__name(setupMidiActivityMixin, "setupMidiActivityMixin");

// src/module/activities/AttackActivity.ts
var MidiAttackSheet;
var MidiAttackActivity;
function getEffectiveForceRollDialog(activity) {
  const activitySetting = activity.midiProperties?.forceRollDialog;
  if (activitySetting === "always" || activitySetting === "never") {
    return activitySetting;
  }
  return "default";
}
__name(getEffectiveForceRollDialog, "getEffectiveForceRollDialog");
function setupAttackActivity() {
  if (debugEnabled > 0) warn("MidiQOL | AttackActivity | setupAttackActivity | Called");
  MidiAttackSheet = defineMidiAttackSheetClass(game.system.applications.activity.AttackSheet);
  MidiAttackActivity = defineMidiAttackActivityClass(GameSystemConfig.activityTypes.attack.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.attack = { documentClass: MidiAttackActivity };
  } else {
    GameSystemConfig.activityTypes["midiAttack"] = { documentClass: MidiAttackActivity };
  }
}
__name(setupAttackActivity, "setupAttackActivity");
var defineMidiAttackSheetClass = /* @__PURE__ */ __name((baseClass) => {
  return class MidiAttackActivitySheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiAttackActivitySheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/attack-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates,
          "modules/midi-qol/templates/activity/parts/attack-extras.hbs"
        ]
      }
    };
    async _prepareEffectContext(context) {
      const activity = this.activity;
      context = await super._prepareEffectContext(context);
      context.attackModeOptions = this.item.system.attackModes;
      context.hasAmmunition = this.item.system.properties.has("amm");
      context.ammunitionOptions = this.item.system.ammunitionOptions ?? [];
      context.ammunitionOptions?.forEach((option) => {
        option.selected = option.value === this.activity.ammunition;
      });
      if (activity.otherActivityUuid) {
        ui.notifications?.warn("Please update other activity. otherActivityUuid is deprecated");
        activity.otherActivityUuid = void 0;
      }
      if (context.damageParts?.length) {
        const healingTypes = Object.entries(GameSystemConfig.healingTypes).map(([value, config]) => ({
          value,
          label: config.label,
          selected: false
        }));
        for (const part of context.damageParts) {
          const typesSet = part.data?.types instanceof Set ? part.data.types : /* @__PURE__ */ new Set();
          for (const healingType of healingTypes) {
            healingType.selected = typesSet.has(healingType.value);
          }
          part.typeOptions = [...part.typeOptions ?? [], ...healingTypes.map((h) => ({ ...h }))];
        }
      }
      context.ConfirmTargetOptions = Object.entries(confirmTargetOptions).map(([value, label]) => ({ value, label }));
      if (debugEnabled > 0) {
        warn("prepareEffectContext | context", context);
      }
      return context;
    }
    _prepareSubmitData(event, formData) {
      let submitData = super._prepareSubmitData(event, formData);
      submitData.otherActivityUuid = "";
      return submitData;
    }
  };
}, "defineMidiAttackSheetClass");
var defineMidiAttackActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiAttackActivity3 extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiAttackActivity");
    }
    _otherActivity;
    static LOCALIZATION_PREFIXES = [...super.LOCALIZATION_PREFIXES, "midi-qol.ATTACK"];
    static defineSchema() {
      const { StringField: StringField2, ArrayField, BooleanField, SchemaField, ObjectField, NumberField, DocumentIdField } = foundry.data.fields;
      const schema = {
        ...super.defineSchema(),
        otherActivityId: new StringField2({ name: "otherActivity", initial: "" }),
        otherActivityAsParentType: new BooleanField({
          name: "otherActivityAsParentType",
          initial: true,
          required: false,
          label: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.label"),
          hint: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.hint")
        }),
        attackMode: new StringField2({ name: "attackMode", initial: "oneHanded" }),
        ammunition: new StringField2({ name: "ammunition", initial: "" }),
        // deprecated 
        otherActivityUuid: new StringField2({ name: "otherActivityUuid", required: false, initial: "" }),
        attackRollPerTarget: new StringField2({ name: "attackRollPerTarget", initial: "default", required: true }),
        fumbleThreshold: new NumberField({ name: "fumbleThreshold", initial: 1, required: false })
      };
      return schema;
    }
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        sheetClass: MidiAttackSheet,
        title: configSettings.activityNamePrefix ? "midi-qol.ATTACK.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          actions: {
            rollAttack: MidiAttackActivity3.#rollAttack,
            rollAttackAdvantage: MidiAttackActivity3.#rollAttackAdvantage,
            rollAttackDisadvantage: MidiAttackActivity3.#rollAttackDisadvantage,
            rollDamage: MidiAttackActivity3.#rollDamage,
            rollDamageNoCritical: MidiAttackActivity3.#rollDamageNoCritical,
            rollDamageCritical: MidiAttackActivity3.#rollDamageCritical
          }
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    static #rollAttack(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (!message || !workflow || workflow.currentAction && [workflow.WorkflowState_Abort, workflow.WorkflowState_Completed].includes(workflow.currentAction)) {
        if (workflow?.itemCardUuid) Workflow.removeItemCardButtons(workflow.itemCardUuid, { removeAllButtons: true });
        const attackConfig = { event, workflow };
        attackConfig.midiOptions = workflow?.rollOptions ?? {};
        attackConfig.midiOptions.workflowOptions ??= {};
        attackConfig.midiOptions.targets = new Set(workflow?.targets);
        attackConfig.workflow = workflow;
        if (workflow) {
          attackConfig.midiOptions.workflowOptions ??= {};
          attackConfig.midiOptions.workflowOptions.autoRollAttack = true;
        }
        attackConfig.event = event;
        this.use(attackConfig, {}, {});
      } else {
        const attackConfig = { workflow };
        attackConfig.midiOptions = workflow?.rollOptions ?? {};
        attackConfig.midiOptions.workflowOptions ??= workflow.workflowOptions ?? {};
        workflow.advantage = void 0;
        workflow.disadvantage = void 0;
        attackConfig.midiOptions.advantage = void 0;
        attackConfig.midiOptions.disadvantage = void 0;
        workflow.activity = this;
        const currentTargets = validTargetTokens(new Set(game.user?.targets));
        if (currentTargets.size > 0) workflow.setTargets(currentTargets);
        return this.rollAttack({ workflow }, {}, {});
      }
    }
    static #rollAttackAdvantage(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (!message || !workflow || workflow.currentAction && [workflow.WorkflowState_Abort, workflow.WorkflowState_Completed].includes(workflow.currentAction)) {
        if (workflow?.itemCardUuid) Workflow.removeItemCardButtons(workflow.itemCardUuid, { removeAllButtons: true });
        const attackConfig = { event, workflow };
        attackConfig.midiOptions = workflow?.rollOptions ?? {};
        attackConfig.midiOptions.workflowOptions ??= {};
        attackConfig.workflow = workflow;
        if (workflow) {
          attackConfig.midiOptions.workflowOptions ??= {};
          attackConfig.midiOptions.workflowOptions.advantage = true;
          attackConfig.midiOptions.workflowOptions.disadvantage = false;
          attackConfig.midiOptions.workflowOptions.autoRollAttack = true;
        }
        this.use(attackConfig, {}, {});
      } else {
        const attackConfig = { workflow };
        attackConfig.midiOptions = workflow?.rollOptions ?? {};
        attackConfig.midiOptions.workflowOptions ??= workflow.workflowOptions ?? {};
        attackConfig.midiOptions.workflowOptions.advantage = true;
        attackConfig.midiOptions.workflowOptions.disadvantage = false;
        workflow.activity = this;
        const currentTargets = validTargetTokens(new Set(game.user?.targets));
        if (currentTargets.size > 0) workflow.setTargets(currentTargets);
        return this.rollAttack(attackConfig, {}, {});
      }
    }
    static #rollAttackDisadvantage(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (!message || !workflow || workflow.currentAction && [workflow.WorkflowState_Abort, workflow.WorkflowState_Completed].includes(workflow.currentAction)) {
        if (workflow?.itemCardUuid) Workflow.removeItemCardButtons(workflow.itemCardUuid, { removeAllButtons: true });
        const attackConfig = { event, workflow };
        attackConfig.midiOptions = workflow?.rollOptions ?? {};
        attackConfig.midiOptions.workflowOptions ??= {};
        attackConfig.workflow = workflow;
        if (workflow) {
          attackConfig.midiOptions.workflowOptions ??= {};
          attackConfig.midiOptions.workflowOptions.autoRollAttack = true;
          attackConfig.midiOptions.advantage = false;
          attackConfig.midiOptions.disadvantage = true;
        }
        this.use(attackConfig, {}, {});
      } else {
        const attackConfig = { workflow };
        attackConfig.midiOptions = workflow?.rollOptions ?? {};
        attackConfig.midiOptions.workflowOptions ??= workflow.workflowOptions ?? {};
        attackConfig.midiOptions.workflowOptions.advantage = false;
        attackConfig.midiOptions.workflowOptions.disadvantage = true;
        workflow.activity = this;
        const currentTargets = validTargetTokens(new Set(game.user?.targets));
        if (currentTargets.size > 0) workflow.setTargets(currentTargets);
        return this.rollAttack(attackConfig, {}, {});
      }
    }
    static #rollDamage(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow, midiOptions: { isCritical: workflow?.workflowOptions?.isCritical || workflow?.isCritical } }, {}, message);
    }
    static #rollDamageNoCritical(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow, critical: { allow: false }, midiOptions: { isCritical: false } }, {}, message);
    }
    static #rollDamageCritical(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow, midiOptions: { isCritical: true } }, {}, message);
    }
    async rollDamage(config, dialog, message) {
      if (!config.ammunition) {
        let ammunition = this.ammunitionItem;
        if (!ammunition) ammunition = config.workflow?.ammunition;
        if (!ammunition && config.workflow) {
          const data = foundry.utils.getProperty(config.workflow.chatCard, "flags.midi-qol.ammunitionData");
          if (data) return new Item.implementation(data, { parent: this.actor });
        }
        config.ammunition = ammunition;
        this.ammunitionItem = ammunition;
      }
      return super.rollDamage(config, dialog, message);
    }
    async _triggerSubsequentActions(config, results) {
    }
    async useWIP(usage = {}, dialog = {}, message = {}) {
      let preValidateRollHookId;
      try {
        if (debugEnabled > 0) warn("MidiQOL | AttackActivity | use | Called", usage, dialog, message);
        usage.midiOptions ??= {};
        if (foundry.utils.getProperty(usage, "midiOptions.checkRollPerTarget") !== false) {
          const itemAttackPerTarget = this.attackRollPerTarget ?? "default";
          let isAttackPerTarget = itemAttackPerTarget === "always" || configSettings.attackPerTarget && itemAttackPerTarget !== "never";
          if (isAttackPerTarget) {
            foundry.utils.setProperty(usage, "midiOptions.workflowOptions.rollAttackPerTarget", true);
          }
        }
        return super.use(usage, dialog, message);
      } finally {
        if (preValidateRollHookId) Hooks.off("dnd5e.preValidateRollAttack", preValidateRollHookId);
      }
    }
    async use(usage = {}, dialog = {}, message = {}) {
      if (debugEnabled > 0) warn("MidiQOL | AttackActivity | use | Called", usage, dialog, message);
      usage.midiOptions ??= {};
      const itemAttackPerTarget = this.attackRollPerTarget ?? "default";
      let isAttackPerTarget = itemAttackPerTarget === "always" || configSettings.attackPerTarget && itemAttackPerTarget !== "never";
      isAttackPerTarget &&= this.target?.template?.type === "";
      const willHaveTargets = (usage.midiOptions?.targetsToUse?.size ?? 0) > 0 || (game.user?.targets?.size ?? 0) > 0 || requiresTargetConfirmation(this, {});
      if (!isAttackPerTarget || !willHaveTargets)
        return super.use(usage, dialog, message);
      if (!usage.midiOptions.targetsToUse && !await preTemplateTargets(this, usage.midiOptions)) return false;
      let returnValue;
      let checkReaction = !usage.midiOptions.isTriggered;
      let checkBonusAction = !usage.midiOptions.isTriggered;
      let targets;
      const localSavedTargetIds = Array.from(game.user?.targets ?? []).map((t) => t.id);
      if (usage.midiOptions.targetsToUse) targets = usage.midiOptions.targetsToUse;
      if (!targets) targets = new Set(game.user?.targets);
      for (let target of targets) {
        let item = this.item.clone({}, { keepId: true });
        let activity = item.system.activities.get(this.id);
        const attackConfig = foundry.utils.deepClone(usage);
        attackConfig.midiOptions ??= {};
        attackConfig.midiOptions.targetsToUse = /* @__PURE__ */ new Set([target]);
        attackConfig.midiOptions.workflowOptions ??= {};
        attackConfig.midiOptions.proceedChecks = { checkReaction, checkBonusAction };
        attackConfig.midiOptions.workflowOptions.targetConfirmation = "never";
        const attackDialog = foundry.utils.deepClone(dialog);
        delete attackDialog.configure;
        returnValue = await super.use.bind(activity)(attackConfig, attackDialog, message);
        if (!returnValue) break;
        checkReaction = false;
        checkBonusAction = false;
      }
      updateUserTargets(localSavedTargetIds);
      return returnValue;
    }
    async rollAttack(config = {}, dialog = {}, message = {}) {
      const hookIds = {};
      let rolls;
      const workflow = config.workflow;
      config.midiOptions ??= {};
      const areKeysPressed = game.system.utils.areKeysPressed;
      try {
        if (debugEnabled > 0) warn("MidiQOL | AttackActivity | rollAttack | Called", config, dialog, message);
        if (workflow)
          workflow.rollOptions.rollToggle = areMidiKeysPressed(config.event, "RollToggle");
        let returnValue = await this.configureAttackRoll(config);
        if (!returnValue)
          return super.rollAttack?.(config, dialog, message) ?? [];
        if (!workflow || workflow?.aborted) return [];
        if (configSettings.allowUseMacro && workflow.workflowOptions.noOnUseMacro !== true) {
          await workflow.callMacros(workflow.item, workflow.onUseMacros?.getMacros("preAttackRoll"), "OnUse", "preAttackRoll");
          if (workflow.ammunition) await workflow.callMacros(workflow.ammunition, workflow.ammunitionOnUseMacros?.getMacros("preAttackRoll"), "OnUse", "preAttackRoll");
        }
        config.event ??= workflow.rollOptions.event;
        let requiresAmmoConfirmation = false;
        if (this.item.system.properties?.has("amm")) {
          const ammoConfirmation = this.confirmAmmunition;
          if (ammoConfirmation.reason) {
            ui.notifications?.warn(ammoConfirmation.reason);
          }
          if (!ammoConfirmation.proceed) {
            if (workflow) workflow.aborted = true;
          }
          requiresAmmoConfirmation = ammoConfirmation.confirm;
        }
        const effectiveForceRollDialog = getEffectiveForceRollDialog(this);
        if (areKeysPressed(config.event, "skipDialogNormal")) {
          dialog.configure = effectiveForceRollDialog === "always";
        } else if (effectiveForceRollDialog === "always") {
          dialog.configure = true;
        } else if (effectiveForceRollDialog === "never") {
          dialog.configure = false;
        } else {
          dialog.configure = !isAutoFastForward2("attack", { workflow });
        }
        if (requiresAmmoConfirmation) {
          dialog.configure = true;
        }
        if (this.item?.system.properties.has("thr") && effectiveForceRollDialog !== "never") {
          dialog.configure = true;
        }
        dialog.options ??= {};
        hookIds["dnd5e.preRollAttack"] = Hooks.once("dnd5e.preRollAttack", (rollConfig, dialogConfig, messageConfig) => {
          if (workflow?.aborted) return false;
          if (workflow?.rollOptions?.rollToggle) dialogConfig.configure = !dialogConfig.configure;
          if (configSettings.checkTwoHanded && ["twoHanded", "offhand"].includes(rollConfig.attackMode)) {
            if (this.actor.system.attributes.ac?.shield > 0) {
              ui.notifications?.warn(i18n("midi-qol.TwoHandedShieldWarning"));
              if (workflow) workflow.aborted = true;
              return false;
            }
          } else if (!rollConfig.attackMode && dialogConfig.options?.attackModeOptions?.length) dialogConfig.configure = true;
          const actType = this.actionType;
          const attackModifierFlags = {
            failFlags: ["fail.all", "fail.attack.all", `fail.attack.${actType}`],
            successFlags: ["success.all", "success.attack.all", `success.attack.${actType}`],
            maxFlags: ["max.attack.all", `max.attack.${actType}`],
            minFlags: ["min.attack.all", `min.attack.${actType}`]
          };
          if (workflow?.attackRollModifierTracker) {
            computeRollModifiers(this.actor, attackModifierFlags, workflow.attackRollModifierTracker, { workflow });
            applyRollModifiers(workflow.attackRollModifierTracker, rollConfig, dialogConfig);
          }
          return true;
        });
        hookIds["dnd5e.rollAttack"] = Hooks.once("dnd5e.rollAttack", (rolls2, { ammoUpdate, subject }) => {
          if (ammoUpdate?.destroy) {
            const data = this.actor.items.get(ammoUpdate.id).toObject();
            addUpdatesCache(workflow.id, { "flags.midi-qol.ammunitionData": data });
          }
        });
        hookIds["dnd5e.postAttackRollConfiguration"] = Hooks.once("dnd5e.postAttackRollConfiguration", (rolls2, config2, dialog2, message2) => {
          if (rolls2?.length === 0) {
            return false;
          }
          if (configSettings.checkTwoHanded && ["twoHanded", "offhand"].includes(rolls2?.[0]?.options?.attackMode)) {
            if (this.actor.system.attributes.ac?.shield > 0) {
              ui.notifications?.warn(i18n("midi-qol.TwoHandedShieldWarning"));
              if (config2.workflow) config2.workflow.aborted = true;
              return false;
            }
          }
          if (this.requireAmmunition && this.item.system.properties?.has("amm")) {
            const chosenAmmunition = this.actor.items.get(rolls2?.[0]?.options?.ammunition);
            if ((chosenAmmunition?.system.quantity ?? 0) <= 0) ui.notifications?.error(i18nFormat2("midi-qol.NoAmmunition", { name: chosenAmmunition?.name ?? "Ammunition" }));
            else this.ammunitionItem = chosenAmmunition;
            return chosenAmmunition?.system.quantity > 0;
          }
          return true;
        });
        hookIds["dnd5e.postRollConfiguration"] = Hooks.once("dnd5e.postRollConfiguration", (rolls2, config2, dialog2, message2) => {
          const targetDescriptors = foundry.utils.getProperty(message2, "data.flags.dnd5e.targets");
          if (targetDescriptors && workflow) {
            workflow.targetDescriptors = targetDescriptors;
          }
        });
        if (workflow.useActiveDefence) {
          message.create = false;
        }
        message ??= {};
        message.create ??= config.midiOptions.chatMessage;
        const rollMode = safeGetGameSetting("core", "rollMode");
        const showChatAttackCard = game.user?.isGM && configSettings.gmAttackDamageCards && [CONST.DICE_ROLL_MODES.SELF, CONST.DICE_ROLL_MODES.BLIND, CONST.DICE_ROLL_MODES.PRIVATE].includes(rollMode);
        if (showChatAttackCard) {
          message.create = true;
          foundry.utils.setProperty(message, "data.flags.midi-qol.gmHide", true);
          message.rollMode = rollMode;
        }
        config.attackMode ??= this.attackMode ?? "oneHanded";
        if (config.event && areMidiKeysPressed(config.event, "Versatile") && this.item.isVersatile) {
          config.attackMode = "twoHanded";
        }
        rolls = await super.rollAttack(config, dialog, message);
        if (!rolls || rolls.length === 0) {
          return null;
        }
        if (dialog.configure && rolls[0]?.options?.ammunition && rolls[0].options.ammunition !== this.ammunition) {
          await this.update({ ammunition: rolls[0].options.ammunition });
          this.ammunition = rolls[0].options.ammunition;
          this.ammunitionItem = this.actor?.items.get(this.ammunition);
          this._otherActivity = void 0;
        }
        if (workflow) {
          const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
          const returnedAdvMode = rolls[0]?.options?.advantageMode;
          if (returnedAdvMode !== void 0) {
            const trackerMode = workflow.attackRollModifierTracker.advantageMode;
            if (returnedAdvMode !== trackerMode) {
              const label = i18n("midi-qol.ConfigButtons");
              if (returnedAdvMode === ADV_MODE.ADVANTAGE) {
                workflow.attackRollModifierTracker.advantage.setOverride("config-buttons", label);
                workflow.attackRollModifierTracker.disadvantage.clearOverride();
              } else if (returnedAdvMode === ADV_MODE.DISADVANTAGE) {
                workflow.attackRollModifierTracker.disadvantage.setOverride("config-buttons", label);
                workflow.attackRollModifierTracker.advantage.clearOverride();
              } else {
                workflow.attackRollModifierTracker.advantage.clearOverride();
                workflow.attackRollModifierTracker.disadvantage.clearOverride();
              }
            }
          }
          workflow.attackMode = rolls[0].options.attackMode ?? config.attackMode;
          if (this.ammunition && !this.ammunitionItem) {
            const storedData = foundry.utils.getProperty(workflow.chatCard, "flags.midi-qol.ammunitionData");
            if (storedData) workflow.ammunition = new Item.implementation(storedData, { parent: this.actor });
          } else workflow.ammunition = this.ammunitionItem;
          if (this.ammunition) this.update({ ammunition: this.ammunitionItem?.id ?? "" });
          if (configSettings.allowUseMacro) {
            workflow.ammunitionOnUseMacros = workflow.ammunition?.flags?.[MODULE_ID]?.onUseMacroParts ?? new OnUseMacros();
          }
          if (workflow.workflowOptions?.attackRollDSN !== false && !showChatAttackCard) await displayDSNForRoll(rolls[0], "attackRollD20");
          await workflow?.setAttackRoll(rolls[0]);
          rolls[0] = await workflow.processAttackRollBonusFlags();
          addAttribution(rolls[0], workflow.attackAttribution);
          await workflow?.setAttackRoll(rolls[0]);
        }
        if (debugEnabled > 0) {
          warn("AttackActivity | rollAttack | setAttackRolls completed ", rolls);
          warn(`Attack Activity | workflow is suspended ${workflow?.suspended}`);
        }
        if (workflow?.suspended) workflow.unSuspend.bind(workflow)({ attackRoll: rolls[0] });
      } catch (err) {
        error2("AttackActivity | rollAttack | Error configuring dialog", err);
      } finally {
        for (const [hookName, hookId] of Object.entries(hookIds)) {
          if (hookId !== void 0) Hooks.off(hookName, hookId);
        }
      }
      return rolls;
    }
    async configureAttackRoll(config) {
      if (debugEnabled > 0) warn("configureAttackRoll", this, config);
      if (!config.workflow) return false;
      let workflow = config.workflow;
      config.midiOptions ??= {};
      if (debugEnabled > 1) debug("Entering configure attack roll", config.event, workflow, config.rollOptions);
      if (workflow.workflowType === "BaseWorkflow") {
        if (workflow.attackRoll && workflow.currentAction === workflow.WorkflowState_Completed) {
          await workflow.setDamageRolls(void 0);
          if (workflow.itemCardUuid) {
            await Workflow.removeItemCardButtons(workflow.itemCardUuid, { removeConfirmButtons: true });
          }
          if (workflow.damageRollCount > 0) {
            const messageConfig = foundry.utils.mergeObject({
              create: true,
              data: {
                flags: {
                  dnd5e: {
                    // @ts-expect-error
                    ...this.messageFlags,
                    messageType: "usage",
                    use: {
                      effects: this.applicableEffects?.map((e) => e.id)
                    }
                  }
                }
              },
              hasConsumption: false
            }, { flags: workflow.chatCard.flags });
            const itemCard = await this._createUsageMessage(messageConfig);
            workflow.itemCardUuid = itemCard.uuid;
            if (configSettings.undoWorkflow && workflow.undoData) {
              workflow.undoData.chatCardUuids = workflow.undoData.chatCardUuids.concat([itemCard.uuid]);
              unTimedExecuteAsGM("updateUndoChatCardUuids", workflow.undoData);
            }
          }
        }
      }
      if (workflow.workflowType === "TrapWorkflow") workflow.workflowOptions.fastForward = true;
      await doActivityReactions(this, workflow);
      await busyWait(10);
      if (configSettings.allowUseMacro && workflow.workflowOptions.noTargetOnUseMacro !== true) {
        await workflow.triggerTargetMacros(["isPreAttacked"]);
        if (workflow.aborted) {
          console.warn(`midi-qol | item ${workflow.item.name ?? ""} roll blocked by isPreAttacked macro`);
          await workflow.performState(workflow.WorkflowState_Abort);
          return false;
        }
      }
      if (await callActivityHooks(this, "midi-qol.preAttackRoll", workflow) === false) {
        console.warn("midi-qol | attack roll blocked by preAttackRoll hook");
        return false;
      }
      await workflow.checkAttackAdvantage();
      workflow.attackRollModifierTracker.processKeys(config);
      if (await callActivityHooks(this, "midi-qol.preAttackRollConfig", workflow) === false) {
        console.warn("midi-qol | attack roll blocked by preAttackRollConfig hook");
        return false;
      }
      if (configSettings.allowUseMacro && workflow.workflowOptions?.noOnUseMacro !== true) {
        await workflow.callMacros(workflow.item, workflow.onUseMacros?.getMacros("preAttackRollConfig"), "OnUse", "preAttackRollConfig");
        if (workflow.ammunition) await workflow.callMacros(workflow.ammunition, workflow.ammunitionOnUseMacros?.getMacros("preAttackRollConfig"), "OnUse", "preAttackRollConfig");
      }
      if (workflow.attackRollModifierTracker.hasAdvantage) config.advantage = true;
      if (workflow.attackRollModifierTracker.hasDisadvantage) config.disadvantage = true;
      workflow.attackRollCount += 1;
      if (workflow.attackRollCount > 1) workflow.damageRollCount = 0;
      config.midiOptions = foundry.utils.mergeObject(
        config.midiOptions,
        {
          chatMessage: ["TrapWorkflow", "Workflow"].includes(workflow.workflowType) ? false : config.midiOptions.chatMessage,
          fastForward: workflow.workflowOptions?.fastForwardAttack ?? workflow.rollOptions.fastForwardAttack ?? config.midiOptions.fastForward,
          messageData: {
            speaker: getSpeaker(this.actor)
          }
        },
        { insertKeys: true, overwrite: true }
      );
      if (workflow.rollOptions.rollToggle) config.midiOptions.fastForward = !config.midiOptions.fastForward;
      const advantageLabels = Array.from(workflow.advReminderAttackAdvAttribution).filter((s) => s.startsWith("ADV:")).map((s) => s.replace("ADV:", ""));
      if (advantageLabels.length > 0) foundry.utils.setProperty(config.midiOptions, "dialogOptions.adv-reminder.advantageLabels", advantageLabels);
      const disadvantageLabels = Array.from(workflow.advReminderAttackAdvAttribution).filter((s) => s.startsWith("DIS:")).map((s) => s.replace("DIS:", ""));
      if (disadvantageLabels.length > 0) foundry.utils.setProperty(config.midiOptions, "dialogOptions.adv-reminder.disadvantageLabels", disadvantageLabels);
      if (config.midiOptions.fumble === true || config.midiOptions.fumble === false)
        delete config.midiOptions.fumble;
      config.midiOptions.chatMessage = false;
      if (config.midiOptions?.workflowOptions?.versatile) config.attackMode = "twoHanded";
      return true;
    }
    getDamageConfig(config) {
      const rollConfig = super.getDamageConfig(config);
      for (let roll of rollConfig.rolls) {
        if (rollConfig.ammunition) {
          let rollProperties = new Set(roll.options.properties);
          const ammunitionProperties = rollConfig.ammunition.system.properties;
          roll.options.properties = Array.from(rollProperties.union(ammunitionProperties));
        }
      }
      return rollConfig;
    }
    /** @override */
    get actionType() {
      const type = this.attack.type;
      return `${type.value === "ranged" ? "r" : "m"}${type.classification === "spell" ? "sak" : "wak"}`;
    }
    set ammunitionItem(value) {
      this._ammunitionItem = value;
    }
    get ammunitionItem() {
      if (this._ammunitionItem) return this._ammunitionItem;
      if (!this.ammunition) return void 0;
      const ammunitionItem = this.actor?.items?.get(this.ammunition);
      return ammunitionItem;
    }
    get possibleOtherActivity() {
      return false;
    }
    get isOtherActivityCompatible() {
      return false;
    }
    get canUseOtherActivity() {
      return true;
    }
    prepareData() {
      super.prepareData();
      if (this.otherActivityUuid && this.otherActivityUuid !== "") {
        console.warn(`midi-qol | otherActivityUuid is deprecated. Edit ${this.actor?.name ?? ""} ${this.item?.name ?? ""} ${this.name} and reset other activity. Currently ${this.otherActivityUuid}`);
      }
    }
    get confirmAmmunition() {
      const ammunitionOptions = this.item.system.ammunitionOptions;
      let ammoAutoSelected = false;
      const ammoCount = (ammunitionOptions?.filter((ammo) => !ammo.disabled) ?? []).length;
      if ((ammoCount ?? 0) > 0 && (this.ammunition ?? "") === "") {
        this.ammunition = ammunitionOptions?.find((ammo) => !ammo.disabled)?.value ?? "";
        ammoAutoSelected = true;
      }
      if (this.requireAmmunition && ammoCount === 0) return { reason: i18n("midi-qol.NoAmmunitionAvailable"), proceed: false, confirm: true };
      if (this.requireAmmunition && !this.ammunition) return { reason: i18n("midi-qol.NoAmmunitionSelected"), proceed: true, confirm: true };
      if (ammunitionOptions.some((ammo) => ammo.value === this.ammunition && ammo.disabled)) return { reason: game.i18n?.format("midi-qol.NoAmmunition", { name: this.ammunitionItem?.name }), proceed: true, confirm: true };
      if (game.user?.isGM) return { confirm: configSettings.gmConfirmAmmunition && (ammoCount > 1 || ammoAutoSelected), proceed: true };
      return { confirm: configSettings.confirmAmmunition && (ammoCount > 1 || ammoAutoSelected), proceed: true };
    }
    get requireAmmunition() {
      return game.user?.isGM ? configSettings.gmRequireAmmunition : configSettings.playerRequireAmmunition;
    }
    async _usageChatContext(message) {
      const context = await super._usageChatContext(message);
      context.hasAttack = this.attack;
      return context;
    }
  };
}, "defineMidiAttackActivityClass");
async function computeAttackAdvantage(actor, actionType, options, abilityId, tracker) {
  tracker ??= new RollModifierTracker();
  const attackSchool = options.item?.system?.school;
  const attackFlags = {
    advantageFlags: [
      "advantage.all",
      "advantage.attack.all",
      `advantage.attack.${actionType}`,
      // Include ability-based attack flags if abilityId is provided
      ...abilityId ? [`advantage.attack.${abilityId}`] : [],
      ...attackSchool ? [`advantage.attack.school.${attackSchool}`] : []
    ],
    disadvantageFlags: [
      "disadvantage.all",
      "disadvantage.attack.all",
      `disadvantage.attack.${actionType}`,
      ...abilityId ? [`disadvantage.attack.${abilityId}`] : [],
      ...attackSchool ? [`disadvantage.attack.school.${attackSchool}`] : []
    ],
    noAdvantageFlags: [
      "noAdvantage.all",
      "noAdvantage.attack.all",
      `noAdvantage.attack.${actionType}`,
      ...abilityId ? [`noAdvantage.attack.${abilityId}`] : [],
      ...attackSchool ? [`noAdvantage.attack.school.${attackSchool}`] : []
    ],
    noDisadvantageFlags: [
      "noDisadvantage.all",
      "noDisadvantage.attack.all",
      `noDisadvantage.attack.${actionType}`,
      ...abilityId ? [`noDisadvantage.attack.${abilityId}`] : [],
      ...attackSchool ? [`noDisadvantage.attack.school.${attackSchool}`] : []
    ],
    systemRollModeFields: [],
    // dnd5e doesn't have native attack roll modes at actor level
    // Target grants flags (checked on target actor)
    sourceAdvantageFlags: [
      "grants.advantage.all",
      "grants.advantage.attack.all",
      `grants.advantage.attack.${actionType}`,
      ...attackSchool ? [`grants.advantage.attack.school.${attackSchool}`] : []
    ],
    sourceDisadvantageFlags: [
      "grants.disadvantage.all",
      "grants.disadvantage.attack.all",
      `grants.disadvantage.attack.${actionType}`,
      ...attackSchool ? [`grants.disadvantage.attack.school.${attackSchool}`] : []
    ],
    sourceNoAdvantageFlags: [
      "grants.noAdvantage.attack.all",
      `grants.noAdvantage.attack.${actionType}`,
      ...attackSchool ? [`grants.noAdvantage.attack.school.${attackSchool}`] : []
    ],
    sourceNoDisadvantageFlags: [
      "grants.noDisadvantage.attack.all",
      `grants.noDisadvantage.attack.${actionType}`,
      ...attackSchool ? [`grants.noDisadvantage.attack.school.${attackSchool}`] : []
    ]
  };
  const targetToken = options.workflow?.targets?.first();
  const targetActor = targetToken?.actor ?? void 0;
  await computeAdvantageFlags(actor, attackFlags, {
    ...options,
    grantsActor: targetActor,
    grantsToken: targetToken
  }, tracker);
  return tracker;
}
__name(computeAttackAdvantage, "computeAttackAdvantage");

// src/module/apps/MidiActivityChoiceDialog.ts
var ActivityChoiceDialog = dnd5e.applications?.activity?.ActivityChoiceDialog;
var pendingChoices = /* @__PURE__ */ new WeakMap();
var MidiActivityChoiceDialog = class _MidiActivityChoiceDialog extends ActivityChoiceDialog {
  static {
    __name(this, "MidiActivityChoiceDialog");
  }
  static get defaultOptions() {
    return super.getDefaultOptions();
  }
  /** The item's offerable activities after midi's usability filter and the module prune hook.
   *  Returns the surviving choice contexts ({id, name, img, sort}), sorted. Single source of truth
   *  for "which activities can be used" — shared by the dialog and the item-use entry points so the
   *  prompt-vs-autoselect-vs-skip decision matches exactly what the dialog would display. */
  static async getActivities(item) {
    const all = [...item.system.activities];
    const riders = item.flags.dnd5e?.riders?.activity ?? [];
    const activities = all.filter((a) => a.canUse !== false && !riders.includes(a.id) && !a.midiProperties?.automationOnly && !a.inProgress).map((a) => ({ id: a.id, name: a.name, img: a.img, sort: a.sort })).sort((a, b) => a.sort - b.sort);
    await asyncHooksCallAll("midi-qol.itemUseActivitySelect", { activities, item });
    return activities;
  }
  /** Resolve the activity to use for an item, applying the prune hook exactly once:
   *  - 0 survivors  → null (caller should skip rolling the item)
   *  - 1 survivor   → that Activity directly, no dialog (unless chooseActivity forces the prompt)
   *  - 2+ survivors → render the picker and resolve to the chosen Activity (or null if dismissed) */
  static async create(item, { chooseActivity, ...options } = {}) {
    const activities = await this.getActivities(item);
    if (activities.length === 0) return null;
    if (activities.length === 1 && !chooseActivity) return item.system.activities.get(activities[0].id);
    pendingChoices.set(item, activities);
    return super.create(item, options);
  }
  // Only here to satisfy the linter
  get item() {
    return super.item;
  }
  async _prepareContext(options) {
    let controlHint;
    if (safeGetGameSetting("dnd5e", "controlHints")) {
      controlHint = i18n("DND5E.Controls.Activity.FastForwardHint");
      controlHint = controlHint.replace(
        "<left-click>",
        `<img src="systems/dnd5e/icons/svg/mouse-left.svg" alt="${i18n("DND5E.Controls.LeftClick")}">`
      );
    }
    const activities = pendingChoices.get(this.item) ?? await _MidiActivityChoiceDialog.getActivities(this.item);
    return {
      ...await super._prepareContext(options),
      controlHint,
      activities
    };
  }
};

// src/module/apps/MidiRollChoiceDialog.ts
var D20RollConfigurationDialog = dnd5e.applications?.dice?.D20RollConfigurationDialog;
var MidiRollChoiceDialog = class _MidiRollChoiceDialog extends D20RollConfigurationDialog {
  static {
    __name(this, "MidiRollChoiceDialog");
  }
  // Only here to satisfy the linter
  static get rollType() {
    return super.rollType;
  }
  get config() {
    return super.config;
  }
  get message() {
    return super.message;
  }
  get rolls() {
    return super.rolls;
  }
  /** Typed access to our custom options (ApplicationV2 options are untyped) */
  get midiOptions() {
    const opts = this.options;
    return {
      rollChoices: opts.rollChoices ?? [],
      choiceType: opts.choiceType ?? "ability",
      advantageByChoice: opts.advantageByChoice
    };
  }
  /** Typed wrapper for ApplicationV2._updateFrame (protected method) */
  _midiUpdateFrame(options) {
    this._updateFrame(options);
  }
  /** Typed wrapper for ApplicationV2.render with parts */
  _midiRenderParts(parts) {
    this.render({ parts });
  }
  /** Mutable default button override — options are frozen by ApplicationV2, so we track it here instead */
  _midiDefaultButton;
  /** @override */
  static DEFAULT_OPTIONS = {
    rollChoices: [],
    advantageByChoice: {},
    choiceType: "ability"
  };
  /* -------------------------------------------- */
  /*  Rendering                                   */
  /* -------------------------------------------- */
  /** @override — restore focus to autofocus button after partial re-render of buttons part */
  _syncPartState(partId, newElement, priorElement, state) {
    super._syncPartState(partId, newElement, priorElement, state);
    if (partId === "buttons") {
      newElement.querySelector("[autofocus]")?.focus();
    }
  }
  /** @override — read defaultButton from mutable _midiDefaultButton when set, instead of frozen options */
  async _prepareButtonsContext(context, options) {
    context = await super._prepareButtonsContext(context, options);
    const overrideButton = this._midiDefaultButton;
    if (overrideButton) {
      for (const [key, btn] of Object.entries(context.buttons ?? {})) {
        btn.default = key === overrideButton;
      }
    }
    return context;
  }
  /** @override */
  async _prepareConfigurationContext(context, options) {
    context = await super._prepareConfigurationContext(context, options);
    const { rollChoices: choices, choiceType } = this.midiOptions;
    if (choiceType === "skill" || choiceType === "tool") {
      context.fields.unshift({
        field: new foundry.data.fields.StringField({
          //@ts-expect-error no dnd5e-types
          required: true,
          blank: false,
          label: game.i18n.localize("DND5E.Abilities")
        }),
        name: "ability",
        options: Object.entries(CONFIG.DND5E.abilities).map(([value, { label }]) => ({ value, label })),
        value: this.config.ability
      });
    }
    if (choices?.length > 1) {
      context.fields.unshift({
        field: new foundry.data.fields.StringField({
          required: true,
          blank: false,
          label: i18n("midi-qol.RollChoice")
        }),
        name: choiceType,
        options: choices.map((c) => ({
          value: c.value,
          label: `${c.label} (${c.modifier})`
        })),
        value: choiceType === "skill" ? this.config.skill : choiceType === "tool" ? this.config.tool : this.config.ability
      });
    }
    return context;
  }
  /* -------------------------------------------- */
  /*  Event Listeners and Handlers                */
  /* -------------------------------------------- */
  /** @override */
  _onChangeForm(formConfig, event) {
    super._onChangeForm(formConfig, event);
    const { choiceType, advantageByChoice } = this.midiOptions;
    if ((choiceType === "skill" || choiceType === "tool") && event.target?.name === "ability") {
      const ability = event.target.value ?? this.config.ability;
      if (this.config.skill) {
        const skillLabel = GameSystemConfig.skills[this.config.skill]?.label ?? "";
        const abilityLabel = GameSystemConfig.abilities[ability]?.label ?? "";
        const flavor = i18nFormat2("DND5E.SkillPromptTitle", { skill: skillLabel, ability: abilityLabel });
        foundry.utils.setProperty(this.message, "data.flavor", flavor);
        this._midiUpdateFrame({ window: { title: flavor } });
      }
    }
    if (event.target?.name === choiceType) {
      const newValue = event.target.value;
      if (choiceType === "tool") {
        this.config.tool = newValue;
        const toolLabel = getToolLabel(newValue);
        const flavor = i18nFormat2("DND5E.ToolPromptTitle", { tool: toolLabel });
        foundry.utils.setProperty(this.message, "data.flavor", flavor);
        this._midiUpdateFrame({ window: { title: flavor } });
      } else if (choiceType === "skill") {
        this.config.skill = newValue;
        const skillLabel = GameSystemConfig.skills[newValue]?.label ?? "";
        const abilityLabel = GameSystemConfig.abilities[this.config.ability]?.label ?? "";
        const flavor = i18nFormat2("DND5E.SkillPromptTitle", { skill: skillLabel, ability: abilityLabel });
        foundry.utils.setProperty(this.message, "data.flavor", flavor);
        this._midiUpdateFrame({ window: { title: flavor } });
      } else {
        this.config.ability = newValue;
        const abilityLabel = GameSystemConfig.abilities[newValue]?.label ?? "";
        const flavor = i18nFormat2("DND5E.SavePromptTitle", { ability: abilityLabel });
        foundry.utils.setProperty(this.message, "data.flavor", flavor);
        this._midiUpdateFrame({ window: { title: flavor } });
      }
      const preComputed = advantageByChoice?.[newValue];
      if (preComputed) {
        this._midiDefaultButton = preComputed.defaultButton;
        this._midiRenderParts(["buttons"]);
      }
    }
  }
  /* -------------------------------------------- */
  /**
   * Factory to create a buildConfig callback for rebuilding save roll parts
   * when the player changes the ability dropdown.
   *
   * This follows the same pattern as dnd5e's `_buildSkillToolConfig` callback
   * used by SkillToolRollConfigurationDialog.
   */
  /**
   * Apply pre-computed advantage/disadvantage to a roll config's options so
   * that D20Roll.fromConfig → configureModifiers() produces the correct formula.
   */
  static applyPreComputedAdvantage(rollConfig, preComputed) {
    if (!preComputed) return;
    if (!preComputed.hasAdvantage && !preComputed.hasDisadvantage) return;
    rollConfig.options ??= {};
    const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
    if (preComputed.hasAdvantage && !preComputed.hasDisadvantage) {
      rollConfig.options.advantageMode = ADV_MODE.ADVANTAGE;
    } else if (!preComputed.hasAdvantage && preComputed.hasDisadvantage) {
      rollConfig.options.advantageMode = ADV_MODE.DISADVANTAGE;
    }
    delete rollConfig.options.advantage;
    delete rollConfig.options.disadvantage;
  }
  /**
   * Unified factory to create a buildConfig callback for rebuilding roll parts
   * when the player changes the dropdown selection.
   *
   * - skill/tool: Delegates to dnd5e's actor._buildSkillToolConfig()
   * - save/check: Custom implementation (dnd5e has no public method for this)
   *
   * @param actor - The actor making the roll
   * @param rollType - "save", "check", "skill", or "tool"
   * @param advantageByChoice - Pre-computed advantage data keyed by choice ID
   */
  static createBuildConfig(actor, rollType, advantageByChoice) {
    return (processConfig, rollConfig, formData, index) => {
      if (index !== 0) return;
      let choiceId;
      if (rollType === "skill" || rollType === "tool") {
        const formField = rollType;
        const newChoice = formData?.get?.(formField) ?? processConfig[formField];
        if (!newChoice || typeof newChoice !== "string") return;
        const hostActor = actor.isPolymorphed && actor.flags?.dnd5e?.transformOptions?.mergeSkills && rollType === "skill" ? game.actors.get(actor.flags.dnd5e?.originalActor) : null;
        actor._buildSkillToolConfig(rollType, hostActor, processConfig, rollConfig, formData, index);
        choiceId = newChoice;
      } else {
        const formAbility = formData?.get?.("ability");
        choiceId = formAbility ?? processConfig.ability;
        if (!choiceId) return;
        if (formAbility && formAbility !== processConfig.ability) {
          const abilityData = actor.system.abilities?.[formAbility];
          if (!abilityData) return;
          const rollData = actor.getRollData();
          const profKey = `${rollType}Prof`;
          const typeCapitalized = rollType.charAt(0).toUpperCase() + rollType.slice(1);
          const { parts, data } = CONFIG.Dice.D20Roll.constructParts({
            mod: abilityData.mod,
            prof: abilityData[profKey]?.hasProficiency ? abilityData[profKey].term : null,
            [`${formAbility}${typeCapitalized}Bonus`]: abilityData.bonuses?.[rollType],
            //@ts-expect-error no dnd5e-types
            [`${rollType}Bonus`]: actor.system.bonuses?.abilities?.[rollType],
            //@ts-expect-error no dnd5e-types
            cover: formAbility === "dex" && rollType === "save" ? actor.system.attributes?.ac?.cover : null
          }, rollData);
          actor.addRollExhaustion?.(parts, data);
          rollConfig.parts = parts;
          rollConfig.data = data;
          processConfig.ability = formAbility;
        }
      }
      _MidiRollChoiceDialog.applyPreComputedAdvantage(rollConfig, advantageByChoice?.[choiceId]);
    };
  }
  // Convenience wrappers for backward compatibility and clearer call sites
  /** @deprecated Use createBuildConfig(actor, "save", advantageByChoice) instead */
  static createSaveBuildConfig(actor, advantageByChoice) {
    return _MidiRollChoiceDialog.createBuildConfig(actor, "save", advantageByChoice);
  }
  /** @deprecated Use createBuildConfig(actor, "check", advantageByChoice) instead */
  static createCheckBuildConfig(actor, advantageByChoice) {
    return _MidiRollChoiceDialog.createBuildConfig(actor, "check", advantageByChoice);
  }
  /** @deprecated Use createBuildConfig(actor, "skill", advantageByChoice) instead */
  static createSkillBuildConfig(actor, advantageByChoice) {
    return _MidiRollChoiceDialog.createBuildConfig(actor, "skill", advantageByChoice);
  }
  /** @deprecated Use createBuildConfig(actor, "tool", advantageByChoice) instead */
  static createToolBuildConfig(actor, advantageByChoice) {
    return _MidiRollChoiceDialog.createBuildConfig(actor, "tool", advantageByChoice);
  }
};

// src/module/patching.ts
var libWrapper;
var NumericTerm = foundry.dice.terms.NumericTerm;
var skipConcentrationActorUuids = /* @__PURE__ */ new Set();
var actorWoundedSemaphores = /* @__PURE__ */ new Map();
function getActorWoundedSemaphore(actorUuid) {
  if (!actorWoundedSemaphores.has(actorUuid)) {
    actorWoundedSemaphores.set(actorUuid, new foundry.utils.Semaphore(1));
  }
  return actorWoundedSemaphores.get(actorUuid);
}
__name(getActorWoundedSemaphore, "getActorWoundedSemaphore");
function initPatching() {
  libWrapper = globalThis.libWrapper;
  const ChatLog = foundry.applications.sidebar.tabs.ChatLog;
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.prepareDerivedData", _prepareDerivedData, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Item.documentClass.prototype.prepareData", itemPrepareData, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.prepareData", actorPrepareData, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.getRollData", actorGetRollData, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Item.documentClass.prototype.getRollData", itemGetRollData, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.ActiveEffect.documentClass.prototype._preCreate", _preCreateActiveEffect, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Dice.D20Roll.prototype.configureModifiers", _configureModifiers, "WRAPPER");
  libWrapper.register(MODULE_ID, "dnd5e.applications.actor.BaseActorSheet.prototype._prepareItem", _prepareItem, "WRAPPER");
  class MidiMeasuredTemplateDocument extends MidiDependentDocumentMixin(
    globalThis.dnd5e.documents.mixins.DependentDocumentMixin(CONFIG.MeasuredTemplate.documentClass)
  ) {
    static {
      __name(this, "MidiMeasuredTemplateDocument");
    }
  }
  ;
  CONFIG.MeasuredTemplate.documentClass = MidiMeasuredTemplateDocument;
  class MidiTokenDocument extends MidiDependentDocumentMixin(
    globalThis.dnd5e.documents.mixins.DependentDocumentMixin(CONFIG.Token.documentClass)
  ) {
    static {
      __name(this, "MidiTokenDocument");
    }
  }
  ;
  CONFIG.Token.documentClass = MidiTokenDocument;
  class MidiAmbientLightDocument extends MidiDependentDocumentMixin(
    globalThis.dnd5e.documents.mixins.DependentDocumentMixin(CONFIG.AmbientLight.documentClass)
  ) {
    static {
      __name(this, "MidiAmbientLightDocument");
    }
  }
  ;
  CONFIG.AmbientLight.documentClass = MidiAmbientLightDocument;
  class MidiRegionDocument extends MidiDependentDocumentMixin(
    globalThis.dnd5e.documents.mixins.DependentDocumentMixin(CONFIG.Region.documentClass)
  ) {
    static {
      __name(this, "MidiRegionDocument");
    }
  }
  ;
  CONFIG.Region.documentClass = MidiRegionDocument;
  class MidiActiveEffect extends MidiDependentDocumentMixin(CONFIG.ActiveEffect.documentClass) {
    static {
      __name(this, "MidiActiveEffect");
    }
  }
  ;
  CONFIG.ActiveEffect.documentClass = MidiActiveEffect;
  class MidiItem extends MidiDependentDocumentMixin(CONFIG.Item.documentClass) {
    static {
      __name(this, "MidiItem");
    }
  }
  ;
  CONFIG.Item.documentClass = MidiItem;
  class MidiActor extends MidiDependentDocumentMixin(CONFIG.Actor.documentClass) {
    static {
      __name(this, "MidiActor");
    }
  }
  ;
  CONFIG.Actor.documentClass = MidiActor;
}
__name(initPatching, "initPatching");
function userInitiatedTargetWrapper(wrapped, ...args) {
  setUserInitiatedTarget(true);
  try {
    return wrapped(...args);
  } finally {
    setUserInitiatedTarget(false);
  }
}
__name(userInitiatedTargetWrapper, "userInitiatedTargetWrapper");
function readyPatching() {
  libWrapper.register(MODULE_ID, "game.dnd5e.canvas.AbilityTemplate.prototype._onMovePlacement", midiATOnMovePlacement, "MIXED");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.sheetClasses.character['dnd5e.CharacterActorSheet'].cls.prototype._filterItems", _filterItems, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.sheetClasses.npc['dnd5e.NPCActorSheet'].cls.prototype._filterItems", _filterItems, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.ActiveEffect.documentClass.createConcentrationEffectData", createConcentrationEffectData, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype._onUpdate", actorOnUpdate, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.challengeConcentration", challengeConcentration, "MIXED");
  libWrapper.register(MODULE_ID, "CONFIG.Combat.documentClass.prototype._preDelete", _preDeleteCombat, "WRAPPER");
  libWrapper.register(MODULE_ID, "foundry.applications.ui.Notifications.prototype.notify", notificationNotify, "MIXED");
  libWrapper.register(MODULE_ID, "foundry.canvas.placeables.Token.prototype._onClickLeft", userInitiatedTargetWrapper, "WRAPPER");
  libWrapper.register(MODULE_ID, "foundry.canvas.placeables.Token.prototype._onClickRight2", userInitiatedTargetWrapper, "WRAPPER");
  libWrapper.register(MODULE_ID, "foundry.canvas.layers.TokenLayer.prototype.selectObjects", userInitiatedTargetWrapper, "WRAPPER");
  const gameVersion = game.system?.version;
  if (game.system?.id === "dnd5e" && foundry.utils.isNewerVersion("3.3", gameVersion ?? "")) {
    if (ui.notifications)
      ui.notifications.error(`dnd5e version ${gameVersion} is too old to support midi-qol, please update to 3.3.1 or later`);
    else
      error2(`dnd5e version ${gameVersion} is too old to support midi-qol, please update to 3.3.1 or later`);
  }
}
__name(readyPatching, "readyPatching");
var visionPatching = /* @__PURE__ */ __name(() => {
  if (playerControlsInvisibleTokens) {
    ui.notifications?.warn("Player control vision is deprecated, use it at your own risk");
    console.warn("midi-qol | Player control vision is deprecated, use it at your own risk");
    log("Patching Token._isVisionSource");
    libWrapper.register(MODULE_ID, "foundry.canvas.placeables.Token.prototype._isVisionSource", _isVisionSource, "WRAPPER");
    log("Patching Token.isVisible");
    libWrapper.register(MODULE_ID, "foundry.canvas.placeables.Token.prototype.isVisible", isVisible, "WRAPPER");
  }
  log("Vision patching - ", playerControlsInvisibleTokens ? "enabled" : "disabled");
}, "visionPatching");
var itemPatching = /* @__PURE__ */ __name(() => {
  libWrapper.register(MODULE_ID, "CONFIG.Item.documentClass.prototype.use", doItemUse, "MIXED");
  libWrapper.register(MODULE_ID, "CONFIG.Dice.DamageRoll.prototype.configureDamage", configureDamage, "MIXED");
}, "itemPatching");
var actorAbilityRollPatching = /* @__PURE__ */ __name(() => {
  log("Patching roll abilities Save/Test/Skill/Tool");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.rollSavingThrow", rollSavingThrow, "MIXED");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.rollAbilityCheck", rollAbilityCheck, "MIXED");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.rollDeathSave", rollDeathSave, "WRAPPER");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.rollSkill", doRollSkill, "MIXED");
  libWrapper.register(MODULE_ID, "CONFIG.Actor.documentClass.prototype.rollToolCheck", rollToolCheck, "WRAPPER");
}, "actorAbilityRollPatching");
async function actorOnUpdate(wrapped, data, options, userId) {
  const hpUpdate = foundry.utils.getProperty(data, "system.attributes.hp.value");
  const temphpUpdate = foundry.utils.getProperty(data, "system.attributes.hp.temp");
  const hpChanging = hpUpdate !== void 0 || temphpUpdate !== void 0;
  if (!hpChanging) {
    await wrapped(data, options, userId);
    return;
  }
  const semaphore = getActorWoundedSemaphore(this.uuid);
  await semaphore.add(async () => {
    await wrapped(data, options, userId);
    if (!game.users?.activeGM?.isSelf) return;
    if (!options.midiVitalityUpdatePending) {
      await processWoundedStatus(this);
    }
  });
}
__name(actorOnUpdate, "actorOnUpdate");
async function processWoundedStatus(actor) {
  const attributes = actor.system.attributes;
  if (!attributes?.hp) return;
  const vitalityItem = getVitalityItem(actor);
  const hasVitality = !!vitalityItem;
  let currentVitality;
  if (vitalityItem) {
    const maxUses = vitalityItem.system.uses?.max ?? 0;
    const spentUses = vitalityItem.system.uses?.spent ?? 0;
    currentVitality = maxUses - spentUses;
  }
  const deathRequires = configSettings.optionalRules.vitalityDeathRequires || "vitalityOnly";
  let needsBeaten;
  if (hasVitality) {
    if (deathRequires === "vitalityOnly") {
      needsBeaten = (currentVitality ?? 0) <= 0;
    } else {
      needsBeaten = (currentVitality ?? 0) <= 0 && attributes.hp.value <= 0;
    }
  } else {
    needsBeaten = attributes.hp.value <= 0;
  }
  if (configSettings.addWounded > 0 && configSettings.addWoundedStyle !== "none") {
    const needsWounded = attributes.hp.pct <= configSettings.addWounded && !needsBeaten;
    const woundedStatus = getWoundedStatus();
    if (!woundedStatus) {
      const message = "wounded status condition not set - please update your midi-qol wounded condition on the mechanics tab";
      TroubleShooter.recordError(new Error(message), "In processWoundedStatus");
      ui.notifications?.warn(`midi-qol | ${message}`);
    } else {
      await actor.toggleStatusEffect(woundedStatus.id, {
        overlay: configSettings.addWoundedStyle === "overlay",
        active: needsWounded
      });
    }
  }
  if (hasVitality) {
    const effect = getVitalityDeadStatus();
    if (effect) {
      await setDeadStatus(actor, { effect, useDefeated: configSettings.markNonPlayerDefeated, makeDead: needsBeaten });
    }
  } else if (configSettings.addDead !== "none") {
    let effect = getDeadStatus();
    let useDefeated = configSettings.markNonPlayerDefeated;
    if (actor.type === "character" || actor.hasPlayerOwner || actor.system.traits?.important) {
      effect = getUnconsciousStatus();
      useDefeated = configSettings.markPlayerDefeated;
    }
    if (effect) {
      await setDeadStatus(actor, { effect, useDefeated, makeDead: needsBeaten });
    }
  }
}
__name(processWoundedStatus, "processWoundedStatus");
function _isVisionSource(wrapped) {
  const isVisionSource = wrapped();
  if (this.document.hidden && !game.user?.isGM && game.user && this.actor?.testUserPermission(game.user, "OWNER")) {
    return true;
  }
  return isVisionSource;
}
__name(_isVisionSource, "_isVisionSource");
function isVisible(wrapped) {
  const isVisible2 = wrapped();
  if (!game.user?.isGM && game.user && this.actor?.testUserPermission(game.user, "OWNER")) {
    return true;
  }
  return isVisible2;
}
__name(isVisible, "isVisible");
function applyBlindRollMode(blindRollSettings, choiceId, messageRollMode) {
  let rollMode = messageRollMode ?? safeGetGameSetting("core", "rollMode");
  if (rollMode === CONST.DICE_ROLL_MODES.BLIND) return rollMode;
  const shouldBeBlind = blindRollSettings.includes("all") || blindRollSettings.includes(choiceId ?? "");
  if (shouldBeBlind && [CONST.DICE_ROLL_MODES.PUBLIC, "roll", CONST.DICE_ROLL_MODES.PRIVATE].includes(rollMode)) {
    rollMode = CONST.DICE_ROLL_MODES.BLIND;
    game.settings.set("core", "rollMode", CONST.DICE_ROLL_MODES.BLIND);
  }
  return rollMode;
}
__name(applyBlindRollMode, "applyBlindRollMode");
var defaultRollOptions = {
  advantage: false,
  disadvantage: false,
  fastForward: void 0,
  fastForwardSet: void 0,
  parts: void 0,
  chatMessage: void 0,
  rollToggle: void 0,
  other: void 0,
  autoRollAttack: void 0,
  autoRollDamage: void 0,
  fastForwardAttack: void 0,
  fastForwardDamage: void 0,
  fastForwardAbility: void 0
};
function collectBonusFlags(actor, category, detail) {
  const bonusFlags = Object.keys(actor.flags[MODULE_ID]?.optional ?? []).filter((flag) => {
    const checkFlag = actor.flags[MODULE_ID].optional[flag][category];
    if (checkFlag === void 0) return false;
    if (detail.startsWith("fail")) {
      const [_, type] = detail.split(".");
      return checkFlag.fail && checkFlag.fail[type] ? getOptionalCountRemainingShortFlag(actor, flag) > 0 : false;
    } else if (!(typeof checkFlag === "string" || checkFlag[detail] || checkFlag["all"] !== void 0)) return false;
    if (actor.flags[MODULE_ID].optional[flag].count === void 0) return true;
    return getOptionalCountRemainingShortFlag(actor, flag) > 0;
  }).map((flag) => {
    const checkFlag = actor.flags[MODULE_ID].optional[flag][category];
    if (typeof checkFlag === "string") return `flags.midi-qol.optional.${flag}`;
    else return `flags.midi-qol.optional.${flag}`;
  });
  return bonusFlags;
}
__name(collectBonusFlags, "collectBonusFlags");
async function bonusCheck(actor, result, category, detail = "", messageData = {}) {
  detail ||= foundry.utils.getProperty(messageData, "flags.dnd5e.roll.type");
  let bonusFlags = collectBonusFlags(actor, category, detail);
  if (bonusFlags.length > 0) {
    const data = {
      actor,
      roll: result,
      rollHTML: await midiRenderRoll(result),
      rollTotal: result.total,
      category,
      detail,
      messageData
    };
    let title;
    if (GameSystemConfig.abilities[detail]?.label || GameSystemConfig.skills[detail]?.label) {
      if (detail.startsWith("fail")) title = "Failed Save Check";
      else if (category.startsWith("check")) title = i18nFormat2("DND5E.AbilityPromptTitle", { ability: GameSystemConfig.abilities[detail].label ?? "" });
      else if (category.startsWith("save")) title = i18nFormat2("DND5E.SavePromptTitle", { ability: GameSystemConfig.abilities[detail].label ?? "" });
      else if (category.startsWith("skill")) title = i18nFormat2("DND5E.SkillPromptTitle", {
        //@ts-expect-error no dnd5e types
        ability: GameSystemConfig.abilities[actor.system.skills[detail]?.ability]?.label ?? "",
        skill: GameSystemConfig.skills[detail].label ?? ""
      });
    } else {
      if (detail.startsWith("fail")) title = "Failed Save Check";
      else if (category.startsWith("check")) title = i18nFormat2("DND5E.AbilityPromptTitle", { ability: GameSystemConfig.abilities[detail] ?? "" });
      else if (category.startsWith("save")) title = i18nFormat2("DND5E.SavePromptTitle", { ability: GameSystemConfig.abilities[detail] ?? "" });
      else if (category.startsWith("skill")) title = i18nFormat2("DND5E.SkillPromptTitle", {
        //@ts-expect-error no dnd5e types
        ability: GameSystemConfig.abilities[actor.system.skills[detail]?.ability] ?? "",
        skill: GameSystemConfig.skills[detail] ?? ""
      });
    }
    const displayBonusRolls = !!(bonusFlags.reduce((returnValue, flag) => returnValue || foundry.utils.getProperty(actor ?? {}, `${flag}.displayBonusRolls`), void 0) ?? checkMechanic("displayBonusRolls"));
    const newRoll = await bonusDialog.bind(data)(
      bonusFlags,
      detail ? `${category}.${detail}` : category,
      displayBonusRolls,
      `${actor.name} - ${title}`,
      data.roll,
      "roll",
      { messageData }
    );
    if (newRoll) result = newRoll;
  }
  return result;
}
__name(bonusCheck, "bonusCheck");
async function doRollSkill(wrapped, config = {}, dialog = {}, message = {}) {
  let oldFormat = false;
  config.midiOptions ??= {};
  let preRollSkillHookId;
  let rollSkillHookId;
  let result = [];
  const saveRollMode = safeGetGameSetting("core", "rollMode");
  try {
    message.data ??= {};
    let skillId = config.skill;
    const rollSkills = config.midiOptions?.rollSkills ?? (skillId ? [skillId] : []);
    if (rollSkills.length === 0)
      throw new Error(`doRollSkill called with no skills to roll for ${this.name}`);
    let overtimeActorUuid;
    if (config.event) {
      const target = config.event?.target?.closest('.roll-link, [data-action="rollRequest"], [data-action="concentration"]');
      if (target?.dataset?.midiOvertimeActorUuid) overtimeActorUuid = target.dataset.midiOvertimeActorUuid;
      if (overtimeActorUuid && this.uuid !== overtimeActorUuid) {
        const actualActor = fromUuidSync(overtimeActorUuid);
        if (actualActor) return actualActor.rollSkill(config, dialog, message);
      }
    }
    const sourceInfo = getAdvantageSourceInfo(config);
    const isExternalModuleRoll = sourceInfo.isExternalModule;
    let hasPreComputedAdvantage = false;
    if (!config.midiOptions.recomputeAdvantage) {
      hasPreComputedAdvantage = applyPreComputedAdvantage(config);
      if (hasPreComputedAdvantage && isAutoFastForward2("skill")) {
        config.midiOptions.fastForward = true;
      }
    }
    if (!config.midiOptions.workflow && config.midiOptions.workflowId) {
      config.midiOptions.workflow = Workflow.getWorkflow(config.midiOptions.workflowId);
    }
    let success = void 0;
    let rollMode = message.rollMode ?? safeGetGameSetting("core", "rollMode");
    if (dialog.configure === void 0 && config.midiOptions.fastForward) dialog.configure = false;
    let skillModifiers;
    const midiOptions = config.midiOptions;
    const passedSkillAdvantageMap = config.midiOptions?.advantageByChoice;
    const hasPassedSkillAdvantage = passedSkillAdvantageMap && Object.keys(passedSkillAdvantageMap).length > 0 && Object.values(passedSkillAdvantageMap).some((v) => v.tracker instanceof RollModifierTracker);
    const preComputedSkillAdvantageMap = isExternalModuleRoll ? {} : hasPassedSkillAdvantage && passedSkillAdvantageMap ? passedSkillAdvantageMap : await buildPreComputedAdvantageMap(this, "skill", rollSkills, config.midiOptions ?? {});
    let userExplicitChoice;
    const { hasMultiple: hasMultipleSkills } = setupChoiceDialog(
      this,
      config,
      dialog,
      "skill",
      rollSkills,
      preComputedSkillAdvantageMap,
      hasPreComputedAdvantage,
      "skill",
      (choice) => {
        userExplicitChoice = choice;
      }
    );
    preRollSkillHookId = Hooks.once("dnd5e.preRollSkill", (configData, dialogData, messageData) => {
      configData.midiOptions = { ...configData.midiOptions ?? {}, ...midiOptions ?? {} };
      messageData.data ??= {};
      if (overtimeActorUuid) messageData.data["flags.midi-qol.overtimeActorUuid"] = overtimeActorUuid;
      const skillAdvantageValues = Object.values(preComputedSkillAdvantageMap);
      const allAutoFail = skillAdvantageValues.length > 0 && skillAdvantageValues.every((d) => {
        const tracker = d.tracker instanceof RollModifierTracker ? d.tracker : void 0;
        return tracker?.modifiers.willFail;
      });
      if (hasMultipleSkills && !allAutoFail && dialogData.configure !== false) {
        dialogData.configure = true;
      } else {
        const chosenSkill = configData.skill ?? skillId;
        const chosenAdvantage = preComputedSkillAdvantageMap[chosenSkill];
        if (chosenAdvantage) {
          if (!hasPreComputedAdvantage) {
            configData.advantage = chosenAdvantage.hasAdvantage;
            configData.disadvantage = chosenAdvantage.hasDisadvantage;
          }
          const chosenTracker = chosenAdvantage.tracker instanceof RollModifierTracker ? chosenAdvantage.tracker : void 0;
          if (chosenTracker) {
            skillModifiers = applyRollModifiers(chosenTracker, configData, dialogData);
            if (skillModifiers.autoFail) success = false;
          }
        }
      }
      setRollDialogOptions(configData, dialogData, "skill");
    });
    rollSkillHookId = Hooks.once("dnd5e.postSkillRollConfiguration", (rolls, configData, dialog2, messageDetails) => {
      message = messageDetails;
      if (configData.skill && configData.skill !== skillId) {
        skillId = configData.skill;
      }
      const chosenSkillData = preComputedSkillAdvantageMap[skillId ?? ""];
      const chosenSkillTracker = chosenSkillData?.tracker instanceof RollModifierTracker ? chosenSkillData.tracker : void 0;
      if (chosenSkillTracker?.modifiers.willFail) success = false;
      else if (chosenSkillTracker?.modifiers.willSucceed) success = true;
      messageDetails.rollMode = applyBlindRollMode(configSettings.rollSkillsBlind, skillId, messageDetails.rollMode);
      return true;
    });
    const saveCreate = message.create;
    message.create = false;
    const originatingCardId = config.midiOptions?.workflow?.itemCardId ?? (config.midiOptions?.itemCardUuid ? fromUuidSync(config.midiOptions.itemCardUuid)?.id : void 0);
    if (originatingCardId) foundry.utils.setProperty(message, "data.flags.dnd5e.originatingMessage", originatingCardId);
    if (this.token) foundry.utils.setProperty(dialog, "options.window.subtitle", this.token.name);
    result = await wrapped(config, dialog, message);
    message.create = saveCreate;
    if (!result) return result;
    rollMode = message.rollMode ?? rollMode;
    result.forEach((r) => r.options.rollMode = rollMode);
    await displayDSNForRoll(result, "skill", rollMode);
    DSNMarkDiceDisplayed(result);
    if (!config.simulate) {
      result[0] = await bonusCheck(this, result[0], "skill", skillId, message.data);
      DSNMarkDiceDisplayed(result);
      if (Number.isNumeric(result[0].options.target) && result[0].isSuccess === false) {
        const failFlagsLength = collectBonusFlags(this, "skill", "fail.all").length;
        const failSkillFlagsLength = collectBonusFlags(this, "skill", `fail.${skillId}`).length;
        if (failFlagsLength || failSkillFlagsLength) {
          result[0] = await bonusCheck(this, result[0], "skill", failSkillFlagsLength ? `fail.${skillId}` : "fail.all", message.data);
          DSNMarkDiceDisplayed(result);
        }
      }
    }
    if (config.target !== void 0 && success === void 0) {
      const resultTotal = result.reduce((acc, r) => acc + r.total, 0);
      success = resultTotal >= config.target;
      result.forEach((r) => r.options.success = success);
    }
    const _chosenSkill = userExplicitChoice ?? config.skill;
    if ((config.midiOptions?.rollSkills?.length ?? 0) > 1) result.forEach((r) => {
      if (r?.options) r.options.midiChosenId = _chosenSkill;
    });
    const rollOriginatingId = foundry.utils.getProperty(message, "data.flags.dnd5e.originatingMessage") ?? originatingCardId;
    if (rollOriginatingId) result.forEach((r) => {
      if (r?.options) r.options.originatingMessage = rollOriginatingId;
    });
    if (message.create !== false && result) {
      if (foundry.utils.getProperty(result, "flags.midi-qol.chatMessageShown") !== true) {
        const messageData = foundry.utils.mergeObject({ sound: CONFIG.sounds.dice }, message.data ?? {});
        messageData.rolls = result;
        const chatMsg = await ChatMessage.create(messageData, { rollMode });
        if (chatMsg) result.forEach((r) => r.parent = chatMsg);
      }
    }
    if (skillId) await expireRollEffect.bind(this)("Skill", skillId, success);
  } catch (err) {
    const message2 = `doRollSkill error ${this.name}, ${this.uuid}`;
    TroubleShooter.recordError(err, message2);
    throw err;
  } finally {
    if (preRollSkillHookId) Hooks.off("dnd5e.preRollSkill", preRollSkillHookId);
    if (rollSkillHookId) Hooks.off("dnd5e.postSkillRollConfiguration", rollSkillHookId);
    if (saveRollMode) game.settings.set("core", "rollMode", saveRollMode);
  }
  return oldFormat ? result?.[0] : result;
}
__name(doRollSkill, "doRollSkill");
function getAdvantageSourceInfo(config) {
  const directAdvantage = config.advantage;
  const directDisadvantage = config.disadvantage;
  const hasDirect = directAdvantage !== void 0 || directDisadvantage !== void 0;
  const eventAdvantage = config.event?.advantage;
  const eventDisadvantage = config.event?.disadvantage;
  const hasEventAdvantage = eventAdvantage !== void 0 || eventDisadvantage !== void 0;
  const rollsAdvantage = config.rolls?.[0]?.options?.advantage;
  const rollsDisadvantage = config.rolls?.[0]?.options?.disadvantage;
  const hasRolls = rollsAdvantage !== void 0 || rollsDisadvantage !== void 0;
  const midiAdvantage = config.midiOptions?.advantage;
  const midiDisadvantage = config.midiOptions?.disadvantage;
  const hasMidiOptions = midiAdvantage !== void 0 || midiDisadvantage !== void 0;
  const advantage = directAdvantage ?? eventAdvantage ?? rollsAdvantage ?? midiAdvantage;
  const disadvantage = directDisadvantage ?? eventDisadvantage ?? rollsDisadvantage ?? midiDisadvantage;
  const hasWorkflowId = !!config.midiOptions?.workflowId;
  const hasExternalSource = hasEventAdvantage && !!installedModules.get("monks-tokenbar") || hasRolls && !hasWorkflowId && !!installedModules.get("epic-rolls-5e") || config._isFlashRoll && !!installedModules.get("flash-rolls-5e");
  let sourceType = "none";
  if (hasEventAdvantage && installedModules.get("monks-tokenbar")) {
    sourceType = "monks-tokenbar";
  } else if (hasRolls && !hasWorkflowId && installedModules.get("epic-rolls-5e")) {
    sourceType = "epic-rolls-5e";
  } else if (config._isFlashRoll && installedModules.get("flash-rolls-5e")) {
    sourceType = "flash-rolls-5e";
  } else if (hasDirect || hasMidiOptions) {
    sourceType = "midi-qol";
  }
  const hasEvent = !!config.event;
  return {
    advantage,
    disadvantage,
    hasPreComputed: hasDirect || hasEventAdvantage || hasRolls || hasMidiOptions,
    isExternalModule: hasExternalSource,
    sourceType,
    hasEvent
  };
}
__name(getAdvantageSourceInfo, "getAdvantageSourceInfo");
function getExternalModuleFastForward(sourceInfo, rollType) {
  switch (sourceInfo.sourceType) {
    case "monks-tokenbar":
      if (installedModules.get("monks-tokenbar")) {
        return safeGetGameSetting("monks-tokenbar", "bypass-roll-dialog") ?? false;
      }
      break;
    case "flash-rolls-5e":
    case "midi-qol":
    case "epic-rolls-5e":
    case "none":
    default:
      return isAutoFastForward2(rollType);
  }
  return isAutoFastForward2(rollType);
}
__name(getExternalModuleFastForward, "getExternalModuleFastForward");
function setRollDialogOptions(config, dialog, rollType) {
  dialog.options ??= {};
  const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
  const sourceInfo = getAdvantageSourceInfo(config);
  if (dialog.configure !== void 0) {
  } else if (sourceInfo.isExternalModule) {
    const moduleFastForward = getExternalModuleFastForward(sourceInfo, rollType);
    dialog.configure = !moduleFastForward;
  } else {
    if (config.midiOptions?.fastForward) dialog.configure = false;
    if (isAutoFastForward2(rollType)) dialog.configure = false;
  }
  if (areMidiKeysPressed(config.event, "RollToggle")) {
    dialog.configure = !dialog.configure;
  }
  const rollOptions = config.rolls?.[0]?.options;
  const hasAdvantage = sourceInfo.advantage || rollOptions?.advantage;
  const hasDisadvantage = sourceInfo.disadvantage || rollOptions?.disadvantage;
  if (hasAdvantage && !hasDisadvantage) {
    dialog.options.advantageMode = ADV_MODE.ADVANTAGE;
    dialog.options.defaultButton = "advantage";
  } else if (!hasAdvantage && hasDisadvantage) {
    dialog.options.advantageMode = ADV_MODE.DISADVANTAGE;
    dialog.options.defaultButton = "disadvantage";
  } else {
    dialog.options.advantageMode = ADV_MODE.NORMAL;
    dialog.options.defaultButton = "normal";
  }
}
__name(setRollDialogOptions, "setRollDialogOptions");
function applyPreComputedAdvantage(config) {
  const sourceInfo = getAdvantageSourceInfo(config);
  if (!sourceInfo.hasPreComputed) {
    return false;
  }
  config.advantage ??= sourceInfo.advantage;
  config.disadvantage ??= sourceInfo.disadvantage;
  return true;
}
__name(applyPreComputedAdvantage, "applyPreComputedAdvantage");
function setupChoiceDialog(actor, config, dialog, choiceType, choices, advantageMap, hasPreComputedAdvantage, rollTypeForChoices, onChoiceCapture) {
  const hasMultiple = choices.length > 1;
  dialog.applicationClass = MidiRollChoiceDialog;
  dialog.options ??= {};
  const rollChoices = buildRollChoices(actor, rollTypeForChoices, choices);
  const best = selectBestRollChoice(rollChoices, advantageMap);
  if (best) {
    if (hasMultiple) config[choiceType] = best.value;
    const bestAdvantage = advantageMap[best.value];
    if (bestAdvantage && !hasPreComputedAdvantage) {
      config.advantage = bestAdvantage.hasAdvantage;
      config.disadvantage = bestAdvantage.hasDisadvantage;
    }
  }
  dialog.options.rollChoices = rollChoices;
  dialog.options.advantageByChoice = advantageMap;
  dialog.options.choiceType = choiceType;
  const needsBuildConfig = choiceType === "skill" || choiceType === "tool" || hasMultiple;
  if (needsBuildConfig) {
    const buildConfigType = choiceType === "skill" || choiceType === "tool" ? choiceType : rollTypeForChoices;
    const baseBuildConfig = MidiRollChoiceDialog.createBuildConfig(actor, buildConfigType, advantageMap);
    dialog.options.buildConfig = (processConfig, rollConfig, formData, index) => {
      baseBuildConfig(processConfig, rollConfig, formData, index);
      if (formData && index === 0) {
        const chosen = formData.get?.(choiceType);
        if (chosen && typeof chosen === "string") onChoiceCapture(chosen);
      }
    };
  }
  if (hasMultiple && dialog.configure !== false) dialog.configure = true;
  return { hasMultiple };
}
__name(setupChoiceDialog, "setupChoiceDialog");
function multiply(modifier) {
  const rgx = /mx([0-9])+/;
  const match = modifier.match(rgx);
  if (!match) return false;
  let [mult] = match.slice(1);
  const multiplier = parseInt(mult);
  for (let r of this.results) {
    r.count = multiplier * r.result;
    r.rerolled = true;
  }
  return true;
}
__name(multiply, "multiply");
function addDiceTermModifiers() {
  const Die = foundry.dice.terms.Die;
  Die.MODIFIERS["mx"] = "multiply";
  foundry.utils.setProperty(Die.prototype, "multiply", multiply);
}
__name(addDiceTermModifiers, "addDiceTermModifiers");
function averageDice(roll) {
  roll.terms = roll.terms.map((term) => {
    if (term instanceof foundry.dice.terms.DiceTerm) {
      const mult = term.modifiers.includes("mx2") ? 2 : 1;
      const newTerm = new NumericTerm({ number: Math.floor((term.number ?? 1) * mult * ((term.faces ?? 1) + 1) / 2) });
      newTerm.options = foundry.utils.duplicate(term.options);
      return newTerm;
    }
    return term;
  });
  roll.resetFormula();
  return roll;
}
__name(averageDice, "averageDice");
function applyDamageModifiers(damageRoll) {
  if (damageRoll.options?.["midi-qol"]?.modifiers?.length) {
    damageRoll.terms.forEach((term) => {
      if (term instanceof foundry.dice.terms.DiceTerm) {
        term.modifiers ??= [];
        term.modifiers = term.modifiers.concat(damageRoll.options["midi-qol"].modifiers);
      }
    });
  }
  return damageRoll;
}
__name(applyDamageModifiers, "applyDamageModifiers");
function configureDamage(wrapped, options = { critical: {} }) {
  this.terms = this.terms.filter((t) => !t.options.criticalBonusDamage && !t.options.criticalFlatBonus);
  this.terms.forEach((term) => {
    if (term._number instanceof Roll) {
      if (!term._number.isDeterministic) return;
      if (!term._number._evaluated) term._number.evaluateSync();
    }
    term.number = term.options.baseNumber ?? term.number;
    if (term.options.baseModifiers) {
      term.modifiers = foundry.utils.deepClone(term.options.baseModifiers);
    }
  });
  this.resetFormula();
  if (!this.configured) applyDamageModifiers(this);
  let useDefaultCritical = getCriticalDamage() === "default";
  useDefaultCritical ||= getCriticalDamage() === "explodeCharacter" && this.data.actorType !== "character";
  useDefaultCritical ||= getCriticalDamage() === "explodeNPC" && this.data.actorType !== "npc";
  const criticalNotAllowed = this.options.critical?.allow === false || options.critical?.allow === false;
  if (criticalNotAllowed || !this.isCritical || useDefaultCritical) {
    const effectiveIsCritical = criticalNotAllowed ? false : this.isCritical;
    options.critical.allow = effectiveIsCritical;
    this.options.isCritical = effectiveIsCritical;
    options.critical.multiplyNumeric ??= game.settings.get("dnd5e", "criticalDamageModifiers");
    options.critical.powerfulCritical ??= game.settings.get("dnd5e", "criticalDamageMaxDice");
    wrapped({ critical: options.critical });
    return;
  }
  this.simplify();
  const OperatorTerm = foundry.dice.terms.OperatorTerm;
  const DiceTerm = foundry.dice.terms.DiceTerm;
  const Die = foundry.dice.terms.Die;
  let bonusTerms = [];
  this.simplify();
  for (let [i, term] of this.terms.entries()) {
    let cm = this.options.critical?.multiplier ?? 2;
    let cb = this.options.critical?.bonusDice && i === 0 ? this.options.critical?.bonusDice : 0;
    if (term._number instanceof Roll) {
      if (!term._number.isDeterministic) continue;
      if (!term._number._evaluated) term._number.evaluateSync();
    }
    if (term.modifiers && !term.options.baseModifiers) {
      term.options.baseModifiers = foundry.utils.deepClone(term.modifiers);
    }
    if (term.modifiers) {
      term.modifiers = foundry.utils.deepClone(term.options.baseModifiers);
    }
    term.options.baseNumber = term.options.baseNumber ?? term.number;
    term.number = term.options.baseNumber;
    switch (getCriticalDamage()) {
      case "maxDamage":
        if (term instanceof DiceTerm) term.modifiers.push(`min${term.faces}`);
        break;
      case "maxDamageExplode":
        if (term instanceof DiceTerm) term.modifiers.push(`min${term.faces}`);
        if (term instanceof DiceTerm) {
          bonusTerms.push(new OperatorTerm({ operator: "+", options: { criticalBonusDamage: true } }));
          const newTerm = new Die({ number: term.number + cb, faces: term.faces });
          newTerm.modifiers.push(`x${term.faces}`);
          newTerm.options = foundry.utils.deepClone(term.options);
          newTerm.options.criticalBonusDamage = true;
          bonusTerms.push(newTerm);
        }
        break;
      case "maxCrit":
      // Powerful critical
      case "maxCritRoll":
        if (term instanceof DiceTerm) {
          let critTerm;
          bonusTerms.push(new OperatorTerm({ operator: "+", options: { criticalBonusDamage: true } }));
          if (getCriticalDamage() === "maxCrit")
            critTerm = new NumericTerm({ number: (term.number + cb) * (term.faces ?? 1) });
          else {
            critTerm = new Die({ number: term.number + cb, faces: term.faces });
            critTerm.modifiers = foundry.utils.deepClone(term.modifiers);
            critTerm.modifiers.push(`min${term.faces}`);
          }
          critTerm.options = foundry.utils.deepClone(term.options);
          critTerm.options.criticalBonusDamage = true;
          bonusTerms.push(critTerm);
        } else if (term instanceof NumericTerm && options.multiplyNumeric) {
          term.number *= cm;
        }
        break;
      case "maxAll":
        if (term instanceof DiceTerm) {
          term.alter(cm, cb);
          term.modifiers.push(`min${term.faces}`);
        } else if (term instanceof NumericTerm && this.options.multiplyNumeric) {
          term.number *= cm;
        }
        break;
      case "bestOfTwo":
        if (term instanceof DiceTerm) {
          term.modifiers.push(`kh${term.number !== 1 ? term.number : ""}`);
          term.number = (term.number ?? 1) * 2;
        }
        break;
      case "doubleDice":
        if (term instanceof DiceTerm) {
          term.modifiers.push("mx2");
        } else if (term instanceof NumericTerm && this.options.multiplyNumeric) {
          term.number *= cm;
        }
        break;
      case "explode":
      case "explodeCharacter":
      case "explodeNPC":
        if (term instanceof DiceTerm) {
          bonusTerms.push(new OperatorTerm({ operator: "+", options: { criticalBonusDamage: true } }));
          const newTerm = new Die({ number: term.number + cb, faces: term.faces });
          newTerm.modifiers.push(`x${term.faces}`);
          newTerm.options = foundry.utils.deepClone(term.options);
          newTerm.options.criticalBonusDamage = true;
          bonusTerms.push(newTerm);
        }
        break;
      case "maxBaseRollCrit":
        if (term instanceof DiceTerm) term.modifiers.push(`min${term.faces}`);
        if (term instanceof DiceTerm) {
          bonusTerms.push(new OperatorTerm({ operator: "+", options: { criticalBonusDamage: true } }));
          const newTerm = new Die({ number: term.number, faces: term.faces });
          newTerm.options = foundry.utils.deepClone(term.options);
          newTerm.options.criticalBonusDamage = true;
          bonusTerms.push(newTerm);
        }
        break;
      case "baseDamage":
      default:
        break;
    }
  }
  if (bonusTerms.length > 0) this.terms.push(...bonusTerms);
  if (this.options.critical?.bonusDamage) {
    const extra = new Roll(this.options.critical.bonusDamage, this.data);
    for (let term of extra.terms) {
      if (term instanceof DiceTerm || term instanceof NumericTerm) {
        if (!term.options?.flavor) term.options = foundry.utils.deepClone(this.terms[0].options);
      }
      term.options.criticalBonusDamage = true;
    }
    if (!(extra.terms[0] instanceof OperatorTerm))
      this.terms.push(new OperatorTerm({ operator: "+", options: { criticalBonusDamage: true } }));
    this.terms.push(...extra.terms);
  }
  while (this.terms.length > 0 && this.terms[this.terms.length - 1] instanceof OperatorTerm)
    this.terms.pop();
  this.resetFormula();
  this.options.configured = true;
}
__name(configureDamage, "configureDamage");
async function doRollAbility(wrapped, rollType, config = {}, dialog = {}, message = {}) {
  let oldFormat = false;
  if (config.midiOptions?.target) config.target = config.midiOptions.target;
  if (config.midiOptions?.isConcentrationCheck) {
    foundry.utils.setProperty(message, "data.flags.midi-qol.isConcentrationCheck", true);
    config.midiOptions.isConcentrationCheck = false;
    return this.rollConcentration(config, dialog, message);
  }
  message.data ??= {};
  config.midiOptions ??= {};
  let abilityId = config.ability;
  const rollAbilities = config.midiOptions?.rollAbilities ?? (abilityId ? [abilityId] : []);
  let overtimeActorUuid;
  let preRollAbilityHookId;
  let rollAbilityHookId;
  const saveRollMode = safeGetGameSetting("core", "rollMode");
  let result;
  let type = "SavingThrow";
  try {
    if (rollAbilities.length === 0 && rollType === "save")
      throw new Error(`doRollAbility called with no abilities to roll for ${this.name}`);
    if (config.event) {
      const target = config?.event?.target?.closest('.roll-link, [data-action="rollRequest"], [data-action="concentration"]');
      if (target?.dataset?.midiOvertimeActorUuid) {
        overtimeActorUuid = target.dataset.midiOvertimeActorUuid;
        message.rollMode = target.dataset.midiRollMode ?? message.rollMode;
      }
      if (overtimeActorUuid && this.uuid !== overtimeActorUuid) {
        const actualActor = fromUuidSync(overtimeActorUuid);
        if (actualActor && rollType === "save")
          return actualActor.rollSavingThrow(config, dialog, message);
        else if (actualActor) return actualActor.rollAbilityCheck(config, dialog, message);
      }
    }
    if (config.target !== void 0 && !checkRule("criticalSaves") && config.midiOptions) {
      config.midiOptions.critical = 21;
      config.midiOptions.fumble = 0;
    }
    const sourceInfo = getAdvantageSourceInfo(config);
    const isExternalModuleRoll = sourceInfo.isExternalModule;
    let hasPreComputedAdvantage = false;
    if (!config.midiOptions.recomputeAdvantage) {
      hasPreComputedAdvantage = applyPreComputedAdvantage(config);
      if (hasPreComputedAdvantage && isAutoFastForward2(rollType)) {
        config.midiOptions.fastForward = true;
      }
    }
    if (!config.midiOptions.workflow && config.midiOptions.workflowId) {
      config.midiOptions.workflow = Workflow.getWorkflow(config.midiOptions.workflowId);
    }
    type = rollType === "save" ? "SavingThrow" : "AbilityCheck";
    let success;
    const saveCreate = message.create;
    message.create = false;
    const originatingCardId = config.midiOptions.workflow?.itemCardId ?? (config.midiOptions.itemCardUuid ? fromUuidSync(config.midiOptions.itemCardUuid)?.id : void 0);
    if (originatingCardId) foundry.utils.setProperty(message, "data.flags.dnd5e.originatingMessage", originatingCardId);
    let abilityModifiers;
    const midiOptions = config.midiOptions;
    const passedAdvantageMap = config.midiOptions?.advantageByChoice;
    const hasPassedAdvantage = passedAdvantageMap && Object.keys(passedAdvantageMap).length > 0 && Object.values(passedAdvantageMap).some((v) => v.tracker instanceof RollModifierTracker);
    const effectiveRollType = rollType === "save" && config.hookNames?.includes("concentration") ? "concentration" : rollType;
    const preComputedAdvantageMap = isExternalModuleRoll ? {} : hasPassedAdvantage && passedAdvantageMap ? passedAdvantageMap : await buildPreComputedAdvantageMap(this, effectiveRollType, rollAbilities, config.midiOptions ?? {});
    let userExplicitChoice;
    const { hasMultiple: hasMultipleChoices } = setupChoiceDialog(
      this,
      config,
      dialog,
      "ability",
      rollAbilities,
      preComputedAdvantageMap,
      hasPreComputedAdvantage,
      rollType,
      (choice) => {
        userExplicitChoice = choice;
      }
    );
    preRollAbilityHookId = Hooks.once(`dnd5e.preRoll${type}`, (configData, dialogData, _message) => {
      configData.midiOptions = { ...configData.midiOptions ?? {}, ...midiOptions ?? {} };
      const advantageValues = Object.values(preComputedAdvantageMap);
      const allAutoFail = advantageValues.length > 0 && advantageValues.every((d) => d.tracker instanceof RollModifierTracker && d.tracker.modifiers.willFail);
      if (hasMultipleChoices && !allAutoFail && dialogData.configure !== false) {
        dialogData.configure = true;
      } else {
        const chosenAbility2 = configData.ability ?? abilityId;
        const chosenAdvantage = preComputedAdvantageMap[chosenAbility2];
        if (chosenAdvantage) {
          if (!hasPreComputedAdvantage) {
            configData.advantage = chosenAdvantage.hasAdvantage;
            configData.disadvantage = chosenAdvantage.hasDisadvantage;
          }
          if (chosenAdvantage.tracker instanceof RollModifierTracker) {
            abilityModifiers = applyRollModifiers(chosenAdvantage.tracker, configData, dialogData);
            if (abilityModifiers.autoFail) success = false;
          }
        }
      }
      setRollDialogOptions(configData, dialogData, rollType);
    });
    rollAbilityHookId = Hooks.once(`dnd5e.post${type}RollConfiguration`, (rolls, configData, dialogData, messageDetails) => {
      message = messageDetails;
      if (configData.ability && configData.ability !== abilityId) {
        abilityId = configData.ability;
      }
      const chosenAbilityData = preComputedAdvantageMap[abilityId ?? ""];
      const chosenAbilityTracker = chosenAbilityData?.tracker instanceof RollModifierTracker ? chosenAbilityData.tracker : void 0;
      if (chosenAbilityTracker?.modifiers.willFail) success = false;
      else if (chosenAbilityTracker?.modifiers.willSucceed) success = true;
      const blindRollSettings = rollType === "check" ? configSettings.rollChecksBlind : configSettings.rollSavesBlind;
      messageDetails.rollMode = applyBlindRollMode(blindRollSettings, abilityId, messageDetails.rollMode);
    });
    if (this.token) foundry.utils.setProperty(dialog, "options.window.subtitle", this.token.name);
    result = await wrapped(config, dialog, message);
    message.create = saveCreate;
    if (!result) return result;
    if (result instanceof Roll) {
      console.warn("midi-qol | doRollAbility: result is a Roll, not an array of Rolls");
      result = [result];
    }
    let rollMode = message.rollMode ?? safeGetGameSetting("core", "rollMode");
    result.forEach((r) => r.options.rollMode = rollMode);
    await displayDSNForRoll(result, rollType, rollMode);
    DSNMarkDiceDisplayed(result);
    foundry.utils.mergeObject(message.data, { "flags": config.flags ?? {} });
    if (overtimeActorUuid) {
      foundry.utils.setProperty(message.data, "flags.midi-qol.overtimeActorUuid", overtimeActorUuid);
    }
    if (!config.midiOptions.simulate) {
      result[0] = await bonusCheck(this, result[0], rollType, abilityId, message.data);
      DSNMarkDiceDisplayed(result);
      if (Number.isNumeric(result[0].options.target) && result[0].isSuccess === false) {
        const failFlagsLength = collectBonusFlags(this, rollType, "fail.all").length;
        const failAbilityFlagsLength = collectBonusFlags(this, rollType, `fail.${abilityId}`).length;
        if (failFlagsLength || failAbilityFlagsLength) {
          result[0] = await bonusCheck(this, result[0], rollType, failAbilityFlagsLength ? `fail.${abilityId}` : "fail.all", message.data);
        }
      }
    }
    const chosenAbility = userExplicitChoice ?? config.ability;
    if (hasMultipleChoices) result.forEach((r) => {
      if (r?.options) r.options.midiChosenId = chosenAbility;
    });
    const rollOriginatingId = foundry.utils.getProperty(message, "data.flags.dnd5e.originatingMessage") ?? originatingCardId;
    if (rollOriginatingId) result.forEach((r) => {
      if (r?.options) r.options.originatingMessage = rollOriginatingId;
    });
    if (message.create !== false && result) {
      if (foundry.utils.getProperty(result, "flags.midi-qol.chatMessageShown") !== true) {
        const messageData = foundry.utils.mergeObject({ sound: CONFIG.sounds.dice }, message.data ?? {});
        messageData.rolls = result;
        const chatMsg = await ChatMessage.create(messageData, { rollMode });
        if (chatMsg) result.forEach((r) => r.parent = chatMsg);
      }
    }
    if (saveRollMode) game.settings.set("core", "rollMode", saveRollMode);
    if (abilityId) await expireRollEffect.bind(this)(rollType, abilityId, success);
    if (config.midiOptions.isConcentrationCheck) {
      if (success === void 0 && result?.[0]?.isSuccess !== void 0) success = result[0].isSuccess;
      expireRollEffect.bind(this)("isConcentrationSave", "", success);
    }
  } catch (err) {
    const message2 = `doRollAbility error ${this.name} ${abilityId} ${rollType} ${this.uuid}`;
    TroubleShooter.recordError(err, message2);
    error2(message2, err);
    throw err;
  } finally {
    if (preRollAbilityHookId) Hooks.off(`dnd5e.preRoll${type}`, preRollAbilityHookId);
    if (rollAbilityHookId) Hooks.off(`dnd5e.post${type}RollConfiguration`, rollAbilityHookId);
    if (saveRollMode) game.settings.set("core", "rollMode", saveRollMode);
  }
  return oldFormat ? result?.[0] : result;
}
__name(doRollAbility, "doRollAbility");
async function rollSavingThrow(wrapped, config = {}, dialog = {}, message = {}) {
  if (config.hookNames?.includes("deathSave")) {
    return wrapped(config, dialog, message);
  }
  return doRollAbility.bind(this)(wrapped, "save", config, dialog, message);
}
__name(rollSavingThrow, "rollSavingThrow");
async function rollAbilityCheck(wrapped, config = {}, dialog = {}, message = {}) {
  return doRollAbility.bind(this)(wrapped, "check", config, dialog, message);
}
__name(rollAbilityCheck, "rollAbilityCheck");
async function rollDeathSave(wrapped, config = {}, dialog = {}, message = {}) {
  config.midiOptions ??= {};
  const deathSaveBonus = this.flags?.[MODULE_ID]?.deathSaveBonus;
  let hasPreComputedAdvantage = false;
  if (!config.midiOptions.recomputeAdvantage) {
    hasPreComputedAdvantage = applyPreComputedAdvantage(config);
    if (hasPreComputedAdvantage && isAutoFastForward2("save")) {
      config.midiOptions.fastForward = true;
    }
  }
  if (!hasPreComputedAdvantage) {
    const advantageFlags = {
      advantageFlags: ["advantage.all", "advantage.deathSave"],
      disadvantageFlags: ["disadvantage.all", "disadvantage.deathSave"],
      // Actor noAdvantage/noDisadvantage flags (prevent actor's own advantage/disadvantage)
      noAdvantageFlags: ["noAdvantage.all", "noAdvantage.deathSave"],
      noDisadvantageFlags: ["noDisadvantage.all", "noDisadvantage.deathSave"],
      systemRollModeFields: ["attributes.death.roll.mode"]
    };
    const tracker = await computeAdvantageFlags(this, advantageFlags, {
      ...config.midiOptions,
      grantsActor: config.midiOptions.workflow?.actor
    });
    config.advantage = tracker.hasAdvantage;
    config.disadvantage = tracker.hasDisadvantage;
  }
  let advantage = config.advantage;
  let disadvantage = config.disadvantage;
  const options = { advantage, disadvantage };
  if (deathSaveBonus) {
    const conditionData = createConditionData({ workflow: void 0, target: void 0, actor: this });
    let bonus;
    if (typeof deathSaveBonus === "number") {
      bonus = deathSaveBonus;
    } else {
      bonus = await evalAllConditionsAsync(this, "flags.midi-qol.deathSaveBonus", conditionData);
    }
    if (bonus) {
      options.parts = [bonus];
    }
  }
  foundry.utils.mergeObject(config.midiOptions, options);
  config.advantage = advantage;
  config.disadvantage = disadvantage;
  const blindSaveRoll = configSettings.rollSavesBlind.includes("all") || configSettings.rollSavesBlind.includes("death");
  const actor = this;
  const rollModifierTracker = new RollModifierTracker();
  const preRollHookId = Hooks.once("dnd5e.preRollDeathSave", (configData, dialogData, messageData) => {
    configData.midiOptions = { ...configData.midiOptions ?? {}, ...config.midiOptions ?? {} };
    configData.advantage = advantage;
    configData.disadvantage = disadvantage;
    const modifierFlags = {
      failFlags: ["fail.all", "fail.deathSave"],
      successFlags: ["success.all", "success.deathSave"],
      maxFlags: [],
      minFlags: []
    };
    computeRollModifiers(actor, modifierFlags, rollModifierTracker);
    const modifiers = applyRollModifiers(rollModifierTracker, configData, dialogData, { failValue: 2, successValue: 19 });
    const { autoFail, autoSuccess } = modifiers;
    for (let roll of configData.rolls ?? []) {
      roll.options ??= {};
      roll.options.advantage = advantage;
      roll.options.disadvantage = disadvantage;
      if (!autoFail && !autoSuccess && configData.midiOptions?.parts?.length) roll.parts = [...roll.parts ?? [], ...configData.midiOptions.parts];
      if (blindSaveRoll) roll.options.rollMode = CONST.DICE_ROLL_MODES.BLIND;
    }
    setRollDialogOptions(configData, dialogData, "save");
    if (blindSaveRoll) messageData.rollMode = CONST.DICE_ROLL_MODES.BLIND;
  });
  try {
    return await wrapped(config, dialog, message);
  } finally {
    if (preRollHookId) Hooks.off("dnd5e.preRollDeathSave", preRollHookId);
  }
}
__name(rollDeathSave, "rollDeathSave");
function deathSaveHook(rolls, { chatString, subject: actor }) {
  if (configSettings.keepRollStats && actor) {
    if (rolls instanceof Roll) rolls = [rolls];
    let successes = 0;
    let failures = 0;
    for (const roll of rolls) {
      if (roll.isCritical) successes += 2;
      else if (roll.isFumble) failures += 2;
      else if (roll.isFailure) failures += 1;
      else successes += 1;
    }
    gameStats.addDeathSaveResult(actor, successes, failures);
  }
  if (chatString === "DND5E.DeathSaveFailure") {
    if (hasCondition(actor, "unconscious")) {
      const _id = CONFIG.statusEffects.find((e) => e.id === "unconscious")?._id;
      const effect = actor.effects.find((e) => e._id === _id);
      if (effect) effect.setFlag("core", "overlay", false);
    }
    const status = getDeadStatus();
    if (!status) return;
    setDeadStatus(actor, { effect: status, useDefeated: true, makeDead: true, overlay: true });
  }
}
__name(deathSaveHook, "deathSaveHook");
async function computeAbilityAdvantage(actor, rollType, abilityId, options, tracker) {
  tracker = tracker ?? new RollModifierTracker();
  const effectiveOptions = { ...options };
  let magicResistance = false;
  let magicVulnerability = false;
  if (rollType === "save" && options.isMagicSave) {
    const magicCheck = await checkMagicResistanceAndVulnerability(actor, abilityId);
    if (magicCheck.advantage) {
      tracker.advantage.add("magicResistance", "Magic Resistance");
    }
    if (magicCheck.disadvantage) {
      tracker.disadvantage.add("magicVulnerability", "Magic Vulnerability");
    }
    magicResistance = magicCheck.magicResistance;
    magicVulnerability = magicCheck.magicVulnerability;
  }
  const abilityFlags = {
    advantageFlags: [
      "advantage.all",
      "advantage.ability.all",
      `advantage.${rollType}.all`,
      `advantage.${rollType}.${abilityId}`
    ],
    disadvantageFlags: [
      "disadvantage.all",
      "disadvantage.ability.all",
      `disadvantage.${rollType}.all`,
      `disadvantage.${rollType}.${abilityId}`
    ],
    // Actor noAdvantage/noDisadvantage flags (prevent actor's own advantage/disadvantage)
    noAdvantageFlags: [
      "noAdvantage.all",
      `noAdvantage.${rollType}.all`,
      `noAdvantage.${rollType}.${abilityId}`
    ],
    noDisadvantageFlags: [
      "noDisadvantage.all",
      `noDisadvantage.${rollType}.all`,
      `noDisadvantage.${rollType}.${abilityId}`
    ],
    systemRollModeFields: [`abilities.${abilityId}.${rollType}.roll.mode`]
  };
  const saveSchool = options.workflow?.saveItem?.system?.school;
  if (rollType === "save" && saveSchool) {
    abilityFlags.advantageFlags.push(`advantage.save.school.${saveSchool}`);
    abilityFlags.disadvantageFlags.push(`disadvantage.save.school.${saveSchool}`);
    abilityFlags.noAdvantageFlags?.push(`noAdvantage.save.school.${saveSchool}`);
    abilityFlags.noDisadvantageFlags?.push(`noDisadvantage.save.school.${saveSchool}`);
  }
  if (rollType === "save" || rollType === "check") {
    abilityFlags.sourceAdvantageFlags = ["grants.advantage.all", `grants.advantage.${rollType}.all`, `grants.advantage.${rollType}.${abilityId}`];
    abilityFlags.sourceDisadvantageFlags = ["grants.disadvantage.all", `grants.disadvantage.${rollType}.all`, `grants.disadvantage.${rollType}.${abilityId}`];
    abilityFlags.sourceNoAdvantageFlags = ["grants.noAdvantage.all", `grants.noAdvantage.${rollType}.all`, `grants.noAdvantage.${rollType}.${abilityId}`];
    abilityFlags.sourceNoDisadvantageFlags = ["grants.noDisadvantage.all", `grants.noDisadvantage.${rollType}.all`, `grants.noDisadvantage.${rollType}.${abilityId}`];
    if (rollType === "save" && saveSchool) {
      abilityFlags.sourceAdvantageFlags.push(`grants.advantage.save.school.${saveSchool}`);
      abilityFlags.sourceDisadvantageFlags.push(`grants.disadvantage.save.school.${saveSchool}`);
      abilityFlags.sourceNoAdvantageFlags.push(`grants.noAdvantage.save.school.${saveSchool}`);
      abilityFlags.sourceNoDisadvantageFlags.push(`grants.noDisadvantage.save.school.${saveSchool}`);
    }
  }
  await computeAdvantageFlags(actor, abilityFlags, {
    ...effectiveOptions,
    grantsActor: effectiveOptions.workflow?.actor
  }, tracker);
  return { tracker, magicResistance, magicVulnerability };
}
__name(computeAbilityAdvantage, "computeAbilityAdvantage");
var rollTypeHandlers = {
  save: {
    computeAdvantage: /* @__PURE__ */ __name(async (actor, id, options) => {
      const result = await computeAbilityAdvantage(actor, "save", id, options);
      return result.tracker;
    }, "computeAdvantage"),
    getModifierFlags: /* @__PURE__ */ __name((_actor, id, options) => {
      const flags = {
        failFlags: ["fail.all", "fail.ability.all", "fail.ability.save.all", `fail.ability.save.${id}`],
        successFlags: ["success.all", "success.ability.all", "success.ability.save.all", `success.ability.save.${id}`],
        maxFlags: ["max.ability.save.all", `max.ability.save.${id}`],
        minFlags: ["min.ability.save.all", `min.ability.save.${id}`]
      };
      const saveSchool = options?.workflow?.saveItem?.system?.school;
      if (saveSchool) {
        flags.failFlags.push(`fail.save.school.${saveSchool}`);
        flags.successFlags.push(`success.save.school.${saveSchool}`);
      }
      return flags;
    }, "getModifierFlags")
  },
  check: {
    computeAdvantage: /* @__PURE__ */ __name(async (actor, id, options) => {
      const result = await computeAbilityAdvantage(actor, "check", id, options);
      return result.tracker;
    }, "computeAdvantage"),
    getModifierFlags: /* @__PURE__ */ __name((_actor, id) => ({
      failFlags: ["fail.all", "fail.ability.all", "fail.ability.check.all", `fail.ability.check.${id}`],
      successFlags: ["success.all", "success.ability.all", "success.ability.check.all", `success.ability.check.${id}`],
      maxFlags: ["max.ability.check.all", `max.ability.check.${id}`],
      minFlags: ["min.ability.check.all", `min.ability.check.${id}`]
    }), "getModifierFlags")
  },
  skill: {
    computeAdvantage: /* @__PURE__ */ __name(async (actor, id, options) => {
      return computeSkillAdvantage(actor, id, options);
    }, "computeAdvantage"),
    getModifierFlags: /* @__PURE__ */ __name((actor, id) => {
      const abilityId = actor.system?.skills?.[id]?.ability ?? "";
      const flags = {
        failFlags: ["fail.all", "fail.skill.all", `fail.skill.${id}`],
        successFlags: ["success.all", "success.skill.all", `success.skill.${id}`],
        maxFlags: ["max.skill.all", `max.skill.${id}`],
        minFlags: ["min.skill.all", `min.skill.${id}`]
      };
      if (configSettings.skillAbilityCheckAdvantage && abilityId) {
        flags.failFlags.push("fail.ability.all", "fail.ability.check.all", `fail.ability.check.${abilityId}`);
        flags.successFlags.push("success.ability.all", "success.ability.check.all", `success.ability.check.${abilityId}`);
        flags.maxFlags.push("max.ability.check.all", `max.ability.check.${abilityId}`);
        flags.minFlags.push("min.ability.check.all", `min.ability.check.${abilityId}`);
      }
      return flags;
    }, "getModifierFlags")
  },
  tool: {
    computeAdvantage: /* @__PURE__ */ __name(async (actor, id, options) => {
      const abilityId = actor.system?.tools?.[id]?.ability || GameSystemConfig.tools[id]?.ability || "";
      return computeToolAdvantage(actor, id, abilityId, options);
    }, "computeAdvantage"),
    getModifierFlags: /* @__PURE__ */ __name((actor, id) => {
      const abilityId = actor.system?.tools?.[id]?.ability || GameSystemConfig.tools[id]?.ability || "";
      const flags = {
        failFlags: ["fail.all", "fail.tool.all", `fail.tool.${id}`],
        successFlags: ["success.all", "success.tool.all", `success.tool.${id}`],
        maxFlags: ["max.tool.all", `max.tool.${id}`],
        minFlags: ["min.tool.all", `min.tool.${id}`]
      };
      if (abilityId) {
        flags.failFlags.push("fail.ability.all", "fail.ability.check.all", `fail.ability.check.${abilityId}`);
        flags.successFlags.push("success.ability.all", "success.ability.check.all", `success.ability.check.${abilityId}`);
        flags.maxFlags.push("max.ability.check.all", `max.ability.check.${abilityId}`);
        flags.minFlags.push("min.ability.check.all", `min.ability.check.${abilityId}`);
      }
      return flags;
    }, "getModifierFlags")
  },
  // Concentration saves are ability saves (default CON) with additional concentration-specific flags
  concentration: {
    computeAdvantage: /* @__PURE__ */ __name(async (actor, id, options) => {
      const abilityId = actor.system?.attributes?.concentration?.ability || id || GameSystemConfig.defaultAbilities?.concentration || "con";
      const saveResult = await computeAbilityAdvantage(actor, "save", abilityId, options);
      const concentrationFlags = {
        advantageFlags: ["advantage.concentration"],
        disadvantageFlags: ["disadvantage.concentration"],
        noAdvantageFlags: ["noAdvantage.concentration"],
        noDisadvantageFlags: ["noDisadvantage.concentration"],
        systemRollModeFields: ["attributes.concentration.roll.mode"]
      };
      await computeAdvantageFlags(actor, concentrationFlags, options, saveResult.tracker);
      return saveResult.tracker;
    }, "computeAdvantage"),
    getModifierFlags: /* @__PURE__ */ __name((actor, id) => {
      const abilityId = actor.system?.attributes?.concentration?.ability || id || GameSystemConfig.defaultAbilities?.concentration || "con";
      const saveModifiers = rollTypeHandlers.save.getModifierFlags(actor, abilityId);
      return {
        failFlags: [...saveModifiers.failFlags, "fail.concentration"],
        successFlags: [...saveModifiers.successFlags, "success.concentration"],
        maxFlags: [...saveModifiers.maxFlags, "max.concentration"],
        minFlags: [...saveModifiers.minFlags, "min.concentration"]
      };
    }, "getModifierFlags")
  }
};
async function buildPreComputedAdvantageMap(actor, rollType, rollIds, options) {
  const result = {};
  const workflow = options.workflow;
  const handler = rollTypeHandlers[rollType];
  if (!handler) {
    console.warn(`midi-qol | buildPreComputedAdvantageMap: Unknown rollType "${rollType}"`);
    return result;
  }
  for (const id of rollIds) {
    const tracker = await handler.computeAdvantage(actor, id, options);
    const modifierFlags = handler.getModifierFlags(actor, id, options);
    computeRollModifiers(actor, modifierFlags, tracker, { workflow });
    const hasAdv = !!tracker.hasAdvantage;
    const hasDisadv = !!tracker.hasDisadvantage;
    result[id] = {
      hasAdvantage: hasAdv,
      hasDisadvantage: hasDisadv,
      defaultButton: hasAdv && !hasDisadv ? "advantage" : !hasAdv && hasDisadv ? "disadvantage" : "normal",
      tracker
    };
  }
  return result;
}
__name(buildPreComputedAdvantageMap, "buildPreComputedAdvantageMap");
async function computeSkillAdvantage(actor, skillId, options, tracker) {
  const abilityId = actor.system?.skills?.[skillId]?.ability ?? "";
  const skillFlags = {
    advantageFlags: [
      "advantage.all",
      "advantage.skill.all",
      `advantage.skill.${skillId}`
    ],
    disadvantageFlags: [
      "disadvantage.all",
      "disadvantage.skill.all",
      `disadvantage.skill.${skillId}`
    ],
    // Actor noAdvantage/noDisadvantage flags (prevent actor's own advantage/disadvantage)
    noAdvantageFlags: [
      "noAdvantage.all",
      "noAdvantage.skill.all",
      `noAdvantage.skill.${skillId}`
    ],
    noDisadvantageFlags: [
      "noDisadvantage.all",
      "noDisadvantage.skill.all",
      `noDisadvantage.skill.${skillId}`
    ],
    systemRollModeFields: [`skills.${skillId}.roll.mode`],
    // Source grants flags for skills (checked on workflow.actor, e.g., caster)
    sourceAdvantageFlags: ["grants.advantage.all", "grants.advantage.skill.all", `grants.advantage.skill.${skillId}`],
    sourceDisadvantageFlags: ["grants.disadvantage.all", "grants.disadvantage.skill.all", `grants.disadvantage.skill.${skillId}`],
    sourceNoAdvantageFlags: ["grants.noAdvantage.all", "grants.noAdvantage.skill.all", `grants.noAdvantage.skill.${skillId}`],
    sourceNoDisadvantageFlags: ["grants.noDisadvantage.all", "grants.noDisadvantage.skill.all", `grants.noDisadvantage.skill.${skillId}`]
  };
  if (abilityId) {
    skillFlags.advantageFlags.push(`advantage.skill.${abilityId}`);
    skillFlags.disadvantageFlags.push(`disadvantage.skill.${abilityId}`);
    skillFlags.noAdvantageFlags?.push(`noAdvantage.skill.${abilityId}`);
    skillFlags.noDisadvantageFlags?.push(`noDisadvantage.skill.${abilityId}`);
    skillFlags.sourceAdvantageFlags?.push(`grants.advantage.skill.${abilityId}`);
    skillFlags.sourceDisadvantageFlags?.push(`grants.disadvantage.skill.${abilityId}`);
    skillFlags.sourceNoAdvantageFlags?.push(`grants.noAdvantage.skill.${abilityId}`);
    skillFlags.sourceNoDisadvantageFlags?.push(`grants.noDisadvantage.skill.${abilityId}`);
    if (configSettings.skillAbilityCheckAdvantage) {
      skillFlags.advantageFlags.push("advantage.ability.all", "advantage.check.all", `advantage.check.${abilityId}`);
      skillFlags.disadvantageFlags.push("disadvantage.ability.all", "disadvantage.check.all", `disadvantage.check.${abilityId}`);
    }
  }
  tracker ??= new RollModifierTracker();
  await computeAdvantageFlags(actor, skillFlags, {
    ...options,
    grantsActor: options.workflow?.actor
  }, tracker);
  return tracker;
}
__name(computeSkillAdvantage, "computeSkillAdvantage");
async function computeToolAdvantage(actor, toolId, abilityId, options, tracker) {
  const toolFlags = {
    advantageFlags: [
      "advantage.all",
      "advantage.tool.all",
      `advantage.tool.${toolId}`
    ],
    disadvantageFlags: [
      "disadvantage.all",
      "disadvantage.tool.all",
      `disadvantage.tool.${toolId}`
    ],
    // Actor noAdvantage/noDisadvantage flags (prevent actor's own advantage/disadvantage)
    noAdvantageFlags: [
      "noAdvantage.all",
      "noAdvantage.tool.all",
      `noAdvantage.tool.${toolId}`
    ],
    noDisadvantageFlags: [
      "noDisadvantage.all",
      "noDisadvantage.tool.all",
      `noDisadvantage.tool.${toolId}`
    ],
    systemRollModeFields: [],
    // dnd5e doesn't have native tool roll modes
    // Source grants flags for tools (checked on workflow.actor, e.g., caster)
    sourceAdvantageFlags: ["grants.advantage.all", "grants.advantage.tool.all", `grants.advantage.tool.${toolId}`],
    sourceDisadvantageFlags: ["grants.disadvantage.all", "grants.disadvantage.tool.all", `grants.disadvantage.tool.${toolId}`],
    sourceNoAdvantageFlags: ["grants.noAdvantage.all", "grants.noAdvantage.tool.all", `grants.noAdvantage.tool.${toolId}`],
    sourceNoDisadvantageFlags: ["grants.noDisadvantage.all", "grants.noDisadvantage.tool.all", `grants.noDisadvantage.tool.${toolId}`]
  };
  if (abilityId) {
    toolFlags.advantageFlags.push("advantage.ability.all", "advantage.check.all", `advantage.check.${abilityId}`);
    toolFlags.disadvantageFlags.push("disadvantage.ability.all", "disadvantage.check.all", `disadvantage.check.${abilityId}`);
    toolFlags.noAdvantageFlags?.push(`noAdvantage.check.all`, `noAdvantage.check.${abilityId}`);
    toolFlags.noDisadvantageFlags?.push(`noDisadvantage.check.all`, `noDisadvantage.check.${abilityId}`);
  }
  tracker ??= new RollModifierTracker();
  await computeAdvantageFlags(actor, toolFlags, {
    ...options,
    grantsActor: options.workflow?.actor
  }, tracker);
  return tracker;
}
__name(computeToolAdvantage, "computeToolAdvantage");
var anchoredMoveTime = 0;
var rangeClampesMoveTime = 0;
function snapTemplatePosition(point) {
  if (!canvas?.grid) return point;
  const M = CONST.GRID_SNAPPING_MODES;
  const mode = configSettings.templateSnapping === "half" ? M.CENTER | M.VERTEX | M.CORNER | M.SIDE_MIDPOINT : M.VERTEX;
  return canvas.grid.getSnappedPoint({ x: point.x, y: point.y }, { mode, resolution: 1 });
}
__name(snapTemplatePosition, "snapTemplatePosition");
function getTemplateMaxRange(templateDoc) {
  const midiFlags2 = templateDoc.flags?.[MODULE_ID];
  return midiFlags2?.maxRange;
}
__name(getTemplateMaxRange, "getTemplateMaxRange");
function clampPointToRange(token, targetX, targetY, maxRange) {
  if (!canvas?.grid || !canvas.dimensions) return { x: targetX, y: targetY, clamped: false };
  const poly = getTokenPerimeterPolygon(token);
  const closest = closestPointOnPerimeter(poly, targetX, targetY);
  const distance = measurePointDistance(closest, { x: targetX, y: targetY });
  if (distance <= maxRange) {
    return { x: targetX, y: targetY, clamped: false };
  }
  const dx = targetX - closest.x;
  const dy = targetY - closest.y;
  if (dx === 0 && dy === 0) return { x: targetX, y: targetY, clamped: false };
  const scale = maxRange / distance;
  return {
    x: closest.x + dx * scale,
    y: closest.y + dy * scale,
    clamped: true
  };
}
__name(clampPointToRange, "clampPointToRange");
function getTokenBorderPoint(token, targetX, targetY) {
  const centerX = token.center.x;
  const centerY = token.center.y;
  const poly = getTokenPerimeterPolygon(token);
  const pts = poly.points;
  const numPts = pts.length;
  if (targetX === centerX && targetY === centerY) {
    let bestX = -Infinity;
    let bestY = centerY;
    for (let i = 0; i < numPts; i += 2) {
      if (pts[i] > bestX) {
        bestX = pts[i];
        bestY = pts[i + 1];
      }
    }
    return { x: bestX, y: bestY };
  }
  const a = { x: centerX, y: centerY };
  const b = { x: targetX, y: targetY };
  let tMin = Infinity;
  let result = { x: centerX, y: centerY };
  for (let i = 0; i < numPts; i += 2) {
    const ni = (i + 2) % numPts;
    const c = { x: pts[i], y: pts[i + 1] };
    const d = { x: pts[ni], y: pts[ni + 1] };
    const intersection = foundry.utils.lineLineIntersection(a, b, c, d, { t1: true });
    if (!intersection) continue;
    const t1 = intersection.t1 ?? 0;
    if (intersection.t0 > 0 && t1 >= 0 && t1 <= 1 && intersection.t0 < tMin) {
      tMin = intersection.t0;
      result = { x: intersection.x, y: intersection.y };
    }
  }
  return result;
}
__name(getTokenBorderPoint, "getTokenBorderPoint");
function getTemplateSourceToken(templateDoc) {
  const midiFlags2 = templateDoc.flags?.[MODULE_ID];
  const sourceTokenUuid = midiFlags2?.sourceTokenUuid;
  if (!sourceTokenUuid) return void 0;
  const tokenDoc = fromUuidSync(sourceTokenUuid);
  return tokenDoc?.object ?? void 0;
}
__name(getTemplateSourceToken, "getTemplateSourceToken");
function midiATOnMovePlacement(wrapped, event) {
  const anchorToToken = this.document.flags?.[MODULE_ID]?.anchorToToken;
  if (anchorToToken) {
    return handleAnchoredTemplateMovement.call(this, event);
  }
  const maxRange = getTemplateMaxRange(this.document);
  const sourceToken = getTemplateSourceToken(this.document);
  if (maxRange && sourceToken) {
    event.stopPropagation();
    const now = Date.now();
    if (now - rangeClampesMoveTime <= 20) return;
    const mouse = event.data?.getLocalPosition(this.layer) ?? event.getLocalPosition(this.layer);
    const position = clampPointToRange(sourceToken, mouse.x, mouse.y, maxRange);
    const snapped2 = snapTemplatePosition(position);
    const updates = { x: snapped2.x, y: snapped2.y };
    const dnd5eFlags = this.document.flags.dnd5e;
    const baseDistance = dnd5eFlags?.dimensions?.size;
    if (dnd5eFlags?.dimensions?.adjustedSize && baseDistance && canvas?.tokens) {
      const rectangle = new PIXI.Rectangle(position.x, position.y, 1, 1);
      const hoveredToken = canvas.tokens.quadtree?.getObjects(rectangle, {
        collisionTest: /* @__PURE__ */ __name(({ t }) => t.visible && !t.document.isSecret, "collisionTest")
      }).first();
      if (hoveredToken) {
        const size = Math.max(hoveredToken.document.width, hoveredToken.document.height);
        updates.distance = baseDistance + size * (canvas.grid?.distance ?? 5) / 2;
      } else {
        updates.distance = baseDistance;
      }
    }
    this.document.updateSource(updates);
    this.refresh();
    rangeClampesMoveTime = now;
    return;
  }
  wrapped(event);
  const snapped = snapTemplatePosition({ x: this.document.x, y: this.document.y });
  if (snapped.x !== this.document.x || snapped.y !== this.document.y) {
    this.document.updateSource({ x: snapped.x, y: snapped.y });
    this.refresh();
  }
}
__name(midiATOnMovePlacement, "midiATOnMovePlacement");
function handleAnchoredTemplateMovement(event) {
  const sourceToken = getTemplateSourceToken(this.document);
  if (!sourceToken) return;
  event.stopPropagation();
  const now = Date.now();
  if (now - anchoredMoveTime <= 20) return;
  const mouse = event.data?.getLocalPosition(this.layer) ?? event.getLocalPosition(this.layer);
  const borderPoint = getTokenBorderPoint(sourceToken, mouse.x, mouse.y);
  const dx = mouse.x - borderPoint.x;
  const dy = mouse.y - borderPoint.y;
  const direction = Math.toDegrees(Math.atan2(dy, dx));
  const snapped = snapTemplatePosition(borderPoint);
  this.document.updateSource({
    x: snapped.x,
    y: snapped.y,
    direction
  });
  this.refresh();
  anchoredMoveTime = now;
}
__name(handleAnchoredTemplateMovement, "handleAnchoredTemplateMovement");
function _prepareDerivedData(wrapped, ...args) {
  wrapped(...args);
  try {
    if (!this.system.abilities?.dex) return;
    if (![false, void 0, "none"].includes(checkRule("challengeModeArmor"))) {
      const armorDetails = this.system.attributes.ac ?? {};
      const ac = armorDetails?.value ?? 10;
      const equippedArmor = armorDetails.equippedArmor;
      let armorAC = equippedArmor?.system.armor.value ?? 10;
      const equippedShield = armorDetails.equippedShield;
      const shieldAC = equippedShield?.system.armor.value ?? 0;
      if (checkRule("challengeModeArmor") !== "challenge") {
        switch (armorDetails.calc) {
          // @ts-expect-error no dnd5e-types
          case "flat":
            armorAC = (ac.flat ?? 10) - this.system.abilities.dex.mod;
            break;
          case "draconic":
            armorAC = 13;
            break;
          // @ts-expect-error no dnd5e-types
          case "natural":
            armorAC = (armorDetails.value ?? 10) - this.system.abilities.dex.mod;
            break;
          case "custom":
            armorAC = equippedArmor?.system.armor.value ?? 10;
            break;
          case "mage":
            armorAC = 13;
            break;
          // perhaps this should be 10 if mage armor is magic bonus
          case "unarmoredMonk":
            armorAC = 10;
            break;
          case "unarmoredBarb":
            armorAC = 10;
            break;
          default:
            armorAC = armorDetails.equippedArmor?.system.armor.value ?? 10;
            break;
        }
        ;
        const armorReduction = armorAC - 10 + shieldAC;
        const ec = ac - armorReduction;
        this.system.attributes.ac.EC = ec;
        this.system.attributes.ac.AR = armorReduction;
      } else {
        if (!this.system.abilities) {
          error2("challenge mode armor failed to find abilities");
          error2(this);
          return;
        }
        let dexMod = this.system.abilities.dex.mod;
        if (equippedArmor?.system.armor.type === "heavy") dexMod = 0;
        if (equippedArmor?.system.armor.type === "medium") dexMod = Math.min(dexMod, 2);
        this.system.attributes.ac.EC = 10 + dexMod + shieldAC;
        this.system.attributes.ac.AR = ac - 10 - dexMod;
      }
    }
  } catch (err) {
    const message = "midi-qol failed to prepare derived data";
    error2(message, err);
    TroubleShooter.recordError(err, message);
  }
}
__name(_prepareDerivedData, "_prepareDerivedData");
function _prepareItem(wrapped, item, ctx) {
  wrapped(item, ctx);
  if (ctx.activities) {
    ctx.activities = ctx.activities.filter((a) => !a.midiProperties?.automationOnly);
  }
}
__name(_prepareItem, "_prepareItem");
function getAttackModifiers(roll) {
  const modifiers = roll.d20.modifiers ?? [];
  const actor = fromUuidSync(roll.data?.actorUuid);
  if (!actor) return modifiers;
  const itemType = roll.data?.item?.itemType;
  const type = roll.data?.activity?.attack?.type;
  if (!itemType || !type) return modifiers;
  const actionType = `${type?.value === "ranged" ? "r" : "m"}${type?.classification === "spell" ? "sak" : "wak"}`;
  const changes = [];
  for (let effect of actor.allApplicableEffects()) {
    if (effect.disabled || effect.isSuppressed) continue;
    for (let change of effect.changes) {
      if (change.key.startsWith("flags.midi-qol.rollModifiers.attack.")) changes.push(change);
    }
  }
  changes.sort((a, b) => a.priority - b.priority);
  for (let change of changes) {
    const [_, __, ___, ____, actionOrItem] = change.key.split(".");
    if (actionOrItem === "all" || actionOrItem === actionType || actionOrItem === itemType) {
      modifiers.push(change.value);
    }
  }
  return modifiers;
}
__name(getAttackModifiers, "getAttackModifiers");
function _configureModifiers(wrapped) {
  wrapped();
  if (this.data?.activity?.type !== "attack") return;
  this.d20.modifiers = getAttackModifiers(this);
}
__name(_configureModifiers, "_configureModifiers");
function actorPrepareData(wrapped) {
  try {
    foundry.utils.setProperty(this, "flags.midi-qol.onUseMacroName", this._source.flags?.[MODULE_ID]?.onUseMacroName);
    if (debugEnabled > 0) for (let effect of this.appliedEffects) {
      for (let change of effect.changes) {
        if (change.key === "flags.midi-qol.onUseMacroName") {
          if (change.mode !== CONST.ACTIVE_EFFECT_MODES.CUSTOM) {
            error2("onUseMacro effect mode is not custom", `Actor ${this.name} Effect: ${effect.name} ${this.uuid}`);
            TroubleShooter.recordError(new Error("onUseMacro effect mode is not custom"), `Actor ${this.name} Effect: ${effect.name} ${this.uuid} `);
            change.mode = CONST.ACTIVE_EFFECT_MODES.CUSTOM;
          }
        }
      }
    }
    processTraits(this);
    wrapped();
    prepareOnUseMacroData(this);
  } catch (err) {
    const message = `actor prepare data ${this?.name}`;
    TroubleShooter.recordError(err, message);
  }
}
__name(actorPrepareData, "actorPrepareData");
function itemPrepareData(wrapped) {
  foundry.utils.setProperty(this, "flags.midi-qol.onUseMacroName", this._source.flags?.[MODULE_ID]?.onUseMacroName);
  const activities = this.system?.activities;
  if (activities) {
    for (const activity of Object.values(activities)) {
      if (activity.friendlySave === "FriendlyFail") activity.friendlySave = "friendlyFail";
      else if (activity.friendlySave === "FriendlySuccess") activity.friendlySave = "friendlySuccess";
    }
  }
  if (debugEnabled > 0) for (let effect of this.effects) {
    for (let change of effect.changes) {
      if (change.key === "flags.midi-qol.onUseMacroName") {
        if (change.mode !== CONST.ACTIVE_EFFECT_MODES.CUSTOM) {
          error2("onUseMacro effect mode is not custom", `Actor: ${this.parent?.name} Item: ${this.name} Effect: ${effect.name} ${this.uuid} `);
          TroubleShooter.recordError(new Error("onUseMacro effect mode is not custom - mode treated as custom"), `Actor: ${this.parent?.name} Item: ${this.name} Effect: ${effect.name} ${this.uuid} `);
          change.mode = CONST.ACTIVE_EFFECT_MODES.CUSTOM;
        }
      }
    }
  }
  wrapped();
  prepareOnUseMacroData(this);
}
__name(itemPrepareData, "itemPrepareData");
function prepareOnUseMacroData(actorOrItem) {
  try {
    const macros = actorOrItem.flags?.[MODULE_ID]?.onUseMacroName;
    foundry.utils.setProperty(actorOrItem, "flags.midi-qol.onUseMacroParts", new OnUseMacros(macros ?? null));
  } catch (err) {
    const message = `midi-qol | failed to prepare onUse macro data ${actorOrItem?.name}`;
    console.warn(message, err);
    TroubleShooter.recordError(err, message);
  }
}
__name(prepareOnUseMacroData, "prepareOnUseMacroData");
function preUpdateItemActorOnUseMacro(itemOrActor, changes, options, userId) {
  try {
    const macroChanges = changes.flags?.[MODULE_ID]?.onUseMacroParts;
    if (!macroChanges || foundry.utils.isEmpty(macroChanges)) return true;
    const macros = itemOrActor._source.flags?.[MODULE_ID]?.onUseMacroName;
    const macroParts = new OnUseMacros(macros ?? null);
    if (!Array.isArray(macroChanges.items)) {
      for (let keyString in macroChanges.items) {
        let key = Number(keyString);
        if (Number.isNaN(key)) continue;
        if (!macroParts.items[key]) {
          macroParts.items.push(OnUseMacro.parsePart({
            macroName: macroChanges.items[key]?.macroName ?? "",
            option: macroChanges.items[key]?.option ?? ""
          }));
          key = macroParts.items.length - 1;
        }
        if (macroChanges.items[keyString].macroName) macroParts.items[key].macroName = macroChanges.items[keyString].macroName;
        if (macroChanges.items[keyString].option) macroParts.items[key].option = macroChanges.items[keyString].option;
      }
    }
    let macroString = OnUseMacros.parseParts(macroParts).items.map((oum) => oum.toString()).join(",");
    if (changes.flags?.[MODULE_ID]?.onUseMacroName) changes.flags[MODULE_ID].onUseMacroName = macroString;
    delete changes.flags?.[MODULE_ID]?.onUseMacroParts;
  } catch (err) {
    delete changes.flags?.[MODULE_ID]?.onUseMacroParts;
    const message = `midi-qol | failed in preUpdateItemActor onUse Macro for ${itemOrActor?.name} ${itemOrActor?.uuid}`;
    console.warn(message, err);
    TroubleShooter.recordError(err, message);
  }
  return true;
}
__name(preUpdateItemActorOnUseMacro, "preUpdateItemActorOnUseMacro");
async function zeroHPExpiry(actor, update, options, user) {
  const hpUpdate = foundry.utils.getProperty(update, "system.attributes.hp.value");
  if (hpUpdate !== 0) return;
  const expiredEffects = [];
  for (let effect of actor.appliedEffects) {
    if (effect.flags?.dae?.specialDuration?.includes("zeroHP")) expiredEffects.push(effect);
  }
  if (expiredEffects.length > 0) await expireEffects(actor, expiredEffects, { "expiry-reason": "midi-qol:zeroHP" });
}
__name(zeroHPExpiry, "zeroHPExpiry");
async function checkWounded(actor, _update, _options, _user, _vitalityValue) {
  const semaphore = getActorWoundedSemaphore(actor.uuid);
  await semaphore.add(async () => {
    await processWoundedStatus(actor);
  });
}
__name(checkWounded, "checkWounded");
async function setDeadStatus(actor, options) {
  let { effect, useDefeated, makeDead } = options;
  if (!effect?.id) return;
  if (makeDead) {
    const unconsciousEffect = actor.effects.find((e) => e.statuses.has("unconscious"));
    if (unconsciousEffect) {
      await actor.toggleStatusEffect("unconscious", { active: false });
    }
  }
  const result = await actor.toggleStatusEffect(effect.id, { overlay: configSettings.addDead === "overlay" || options.overlay, active: makeDead });
  if (useDefeated && result !== void 0) {
    const combatants = game.combat?.getCombatantsByActor(actor) ?? [];
    for (let combatant of combatants) await combatant.update({ defeated: makeDead }, {});
  }
}
__name(setDeadStatus, "setDeadStatus");
function createConcentrationEffectData(wrapped, activity, data = {}) {
  const effectData = wrapped(activity, data);
  if (!effectData.flags?.dnd5e?.itemUuid) {
    foundry.utils.setProperty(effectData, "flags.dnd5e.itemUuid", activity.uuid);
  }
  return effectData;
}
__name(createConcentrationEffectData, "createConcentrationEffectData");
async function challengeConcentration(wrapped, { dc = 10, ability = null } = {}) {
  if (skipConcentrationActorUuids.delete(this.uuid)) {
    return;
  }
  if (["chatOnly"].includes(configSettings.doConcentrationCheck))
    return wrapped({ dc, ability });
  const isConcentrating = this.concentration.effects.size > 0;
  if (!isConcentrating) return null;
  if (configSettings.concentrationIncapacitatedConditionCheck && (hasCondition(this, "incapacitated") || this.system.attributes.hp.value <= 0)) return;
  if (["chat"].includes(configSettings.doConcentrationCheck)) {
    const dataset = {
      action: "concentration",
      dc
    };
    if (ability && ability in game.system?.config.abilities) dataset.ability = ability;
    const config = {
      type: "concentration",
      format: "short",
      icon: true
    };
    const enrichers = game.system?.enrichers;
    return ChatMessage.implementation.create({
      content: `<div class="dnd5e chat-card request-card" data-action="concentration" data-dc="${dc}" data-type="midi-concentration">
      <div><span class="visible-dc">${enrichers.createRollLabel({ ...dataset, ...config })} ${i18n("DND5E.Roll")}</span></div>
      <div><span class="hidden-dc">${enrichers.createRollLabel({ ...dataset, ...config, hideDC: true })} ${i18n("DND5E.Roll")}</span></div>
      </div>`,
      whisper: game.users?.filter((user) => this.testUserPermission(user, "OWNER")).map((user) => user.id),
      speaker: ChatMessage.implementation.getSpeaker({ actor: this })
    });
  }
  return;
}
__name(challengeConcentration, "challengeConcentration");
async function checkDeleteTemplate(templateDocument, options, user) {
  if (user !== game.user?.id) return;
  if (options.undo) return;
  if (preferredActiveGM()?.isSelf) {
    await MidiDependentsRegistry.deleteDependents(templateDocument.uuid);
  }
  if (options.noConcentrationCheck || configSettings.removeConcentrationEffects === "none") return;
  const dependentOn = templateDocument.getFlag("dnd5e", "dependentOn");
  if (!dependentOn) return;
  const parentEffect = fromUuidSync(dependentOn);
  if (parentEffect instanceof ActiveEffect) {
    if (parentEffect.getDependents()?.length === 0) {
      await parentEffect.delete();
    }
  }
}
__name(checkDeleteTemplate, "checkDeleteTemplate");
async function rollToolCheck(wrapped, config = {}, dialog = {}, message = {}) {
  let result;
  let preRollHookId;
  let postRollHookId;
  try {
    config.midiOptions ??= {};
    message.data ??= {};
    let toolId = config.tool;
    const rollTools = config.midiOptions?.rollTools ?? (toolId ? [toolId] : []);
    if (rollTools.length === 0)
      throw new Error(`rollToolCheck called with no tools to roll for ${this.name}`);
    const chatMessage = message.create;
    message.create = false;
    const originatingCardId = config.midiOptions?.workflow?.itemCardId ?? (config.midiOptions?.itemCardUuid ? fromUuidSync(config.midiOptions.itemCardUuid)?.id : void 0);
    if (originatingCardId) foundry.utils.setProperty(message, "data.flags.dnd5e.originatingMessage", originatingCardId);
    if (dialog.configure === void 0 && isAutoFastForward2("tool")) dialog.configure = false;
    const sourceInfo = getAdvantageSourceInfo(config);
    const isExternalModuleRoll = sourceInfo.isExternalModule;
    let hasPreComputedAdvantage = false;
    if (!config.midiOptions.recomputeAdvantage) {
      hasPreComputedAdvantage = applyPreComputedAdvantage(config);
      if (hasPreComputedAdvantage && isAutoFastForward2("tool")) {
        config.midiOptions.fastForward = true;
      }
    }
    if (!hasPreComputedAdvantage && !isExternalModuleRoll) {
      if (!config.midiOptions.workflow && config.midiOptions.workflowId) {
        config.midiOptions.workflow = Workflow.getWorkflow(config.midiOptions.workflowId);
      }
      const toolRollModifierTracker = await computeToolAdvantage(this, config.tool ?? "", config.ability ?? "", config.midiOptions);
      config.advantage = toolRollModifierTracker.hasAdvantage;
      config.disadvantage = toolRollModifierTracker.hasDisadvantage;
    }
    let toolModifiers;
    const midiOptions = config.midiOptions;
    let success = void 0;
    const passedToolAdvantageMap = config.midiOptions?.advantageByChoice;
    const hasPassedToolAdvantage = passedToolAdvantageMap && Object.keys(passedToolAdvantageMap).length > 0 && Object.values(passedToolAdvantageMap).some((v) => v.tracker instanceof RollModifierTracker);
    const preComputedToolAdvantageMap = isExternalModuleRoll ? {} : hasPassedToolAdvantage && passedToolAdvantageMap ? passedToolAdvantageMap : await buildPreComputedAdvantageMap(this, "tool", rollTools, config.midiOptions ?? {});
    let userExplicitChoice;
    const { hasMultiple: hasMultipleTools } = setupChoiceDialog(
      this,
      config,
      dialog,
      "tool",
      rollTools,
      preComputedToolAdvantageMap,
      hasPreComputedAdvantage,
      "tool",
      (choice) => {
        userExplicitChoice = choice;
      }
    );
    preRollHookId = Hooks.once("dnd5e.preRollTool", (configData, dialogData, messageData) => {
      configData.midiOptions = { ...configData.midiOptions ?? {}, ...midiOptions ?? {} };
      const toolAdvantageValues = Object.values(preComputedToolAdvantageMap);
      const allAutoFail = toolAdvantageValues.length > 0 && toolAdvantageValues.every((d) => d.tracker instanceof RollModifierTracker && d.tracker.modifiers.willFail);
      if (hasMultipleTools && !allAutoFail && dialogData.configure !== false) {
        dialogData.configure = true;
      } else {
        const chosenTool = configData.tool ?? toolId;
        const chosenAdvantage = preComputedToolAdvantageMap[chosenTool];
        if (chosenAdvantage) {
          if (!hasPreComputedAdvantage) {
            configData.advantage = chosenAdvantage.hasAdvantage;
            configData.disadvantage = chosenAdvantage.hasDisadvantage;
          }
          if (chosenAdvantage.tracker instanceof RollModifierTracker) {
            toolModifiers = applyRollModifiers(chosenAdvantage.tracker, configData, dialogData);
          }
        }
      }
      setRollDialogOptions(configData, dialogData, "tool");
      message = messageData;
    });
    postRollHookId = Hooks.once("dnd5e.postToolRollConfiguration", (rolls, configData, dialogData, messageDetails) => {
      message = messageDetails;
      if (configData.tool && configData.tool !== toolId) {
        toolId = configData.tool;
      }
      const chosenToolData = preComputedToolAdvantageMap[toolId ?? ""];
      const chosenToolTracker = chosenToolData?.tracker instanceof RollModifierTracker ? chosenToolData.tracker : void 0;
      if (chosenToolTracker?.modifiers.willFail) success = false;
      else if (chosenToolTracker?.modifiers.willSucceed) success = true;
      messageDetails.rollMode = applyBlindRollMode(configSettings.rollChecksBlind, toolId, messageDetails.rollMode);
      return true;
    });
    if (this.token) foundry.utils.setProperty(dialog, "options.window.subtitle", this.token.name);
    result = await wrapped(config, dialog, message);
    let rollMode = message.rollMode ?? safeGetGameSetting("core", "rollMode");
    if (result) {
      await displayDSNForRoll(result, "toolCheck", rollMode);
      DSNMarkDiceDisplayed(result);
      result[0] = await bonusCheck(this, result[0], "check", config.ability ?? "", message.data);
      DSNMarkDiceDisplayed(result);
    }
    if (!result) return result;
    const _chosenTool = userExplicitChoice ?? config.tool;
    if (hasMultipleTools) result.forEach((r) => {
      if (r?.options) r.options.midiChosenId = _chosenTool;
    });
    const rollOriginatingId = foundry.utils.getProperty(message, "data.flags.dnd5e.originatingMessage") ?? originatingCardId;
    if (rollOriginatingId) result.forEach((r) => {
      if (r?.options) r.options.originatingMessage = rollOriginatingId;
    });
    if (chatMessage !== false && result) {
      if (foundry.utils.getProperty(result, "flags.midi-qol.chatMessageShown") !== true) {
        const messageData = foundry.utils.mergeObject({ sound: CONFIG.sounds.dice }, message.data ?? {});
        messageData.rolls = result;
        const chatMsg = await ChatMessage.create(messageData, { rollMode });
        if (chatMsg) result.forEach((r) => r.parent = chatMsg);
      }
    }
    return result;
  } catch (err) {
    const message2 = "midi-qol | error in rollToolCheck";
    TroubleShooter.recordError(err, message2);
    throw err;
  } finally {
    if (preRollHookId) Hooks.off("dnd5e.preRollTool", preRollHookId);
    if (postRollHookId) Hooks.off("dnd5e.postToolRollConfiguration", postRollHookId);
  }
}
__name(rollToolCheck, "rollToolCheck");
async function _preCreateActiveEffect(wrapped, data, options, user) {
  try {
    if (!configSettings.concentrationIncapacitatedConditionCheck) return;
    const parent = this.parent;
    const checkConcentration = configSettings.removeConcentration || !safeGetGameSetting("dnd5e", "disableConcentration");
    if (!checkConcentration || options.noConcentrationCheck) return;
    if (!(parent instanceof CONFIG.Actor.documentClass)) return;
    if (globalThis.MidiQOL.incapacitatedConditions.some((condition) => this.statuses.has(condition))) {
      if (debugEnabled > 0) warn(`on createActiveEffect ${this.name} ${this.id} removing concentration for ${parent.name}`);
      if (parent.system.attributes?.hp?.value > 0) {
        await parent.endConcentration();
      }
    }
  } catch (err) {
    const message = "midi-qol | error in preCreateActiveEffect";
    error2(message, err);
    TroubleShooter.recordError(err, message);
  } finally {
    return wrapped(data, options, user);
  }
}
__name(_preCreateActiveEffect, "_preCreateActiveEffect");
async function _preDeleteCombat(wrapped, ...args) {
  try {
    for (let combatant of this.combatants) {
      if (combatant.actor) {
        if (hasUsedReaction(combatant.actor)) await removeReactionUsed(combatant.actor, true);
        if (hasUsedBonusAction(combatant.actor)) await removeBonusActionUsed(combatant.actor, true);
        if (hasUsedAction(combatant.actor)) await removeActionUsed(combatant.actor);
      }
    }
  } catch (err) {
    const message = `midi-qol | error in preDeleteCombat`;
    console.warn(message, err);
    TroubleShooter.recordError(err, message);
  } finally {
    return wrapped(...args);
  }
}
__name(_preDeleteCombat, "_preDeleteCombat");
async function doItemUse(wrapped, config = {}, dialog = {}, message = {}) {
  if (this.pack) return;
  const { chooseActivity, ...activityConfig } = config;
  const activities = this.system.activities?.filter(
    (a) => a.canUse !== false && !this.flags.dnd5e?.riders?.activity?.includes(a.id) && !a.midiProperties?.automationOnly && !a.inProgress
  );
  const attackActivities = activities?.filter((a) => a instanceof MidiAttackActivity);
  if (attackActivities?.length === 1) {
    const attackActivity = attackActivities[0];
    const extraActivities = activities?.filter((a) => a !== attackActivity && a !== attackActivity?.otherActivity);
    if (extraActivities?.length === 0) {
      return await attackActivity.use(config, dialog, message);
    }
  }
  const areKeysPressed = game.system?.utils.areKeysPressed;
  const skipPressed = areKeysPressed(config.event, "skipDialogAdvantage") || areKeysPressed(config.event, "skipDialogDisadvantage") || areKeysPressed(config.event, "skipDialogNormal");
  if (skipPressed) {
    const attackActivity = attackActivities[0];
    if (attackActivity) return await attackActivity.use(config, dialog, message);
    else return await activities?.[0]?.use(config, dialog, message);
  }
  if (activities?.length) {
    const activity = await MidiActivityChoiceDialog.create(this, { chooseActivity });
    return await activity?.use(config, dialog, message);
  }
  if (this.actor) return this.displayCard(message);
}
__name(doItemUse, "doItemUse");
function processTraits(actor) {
  try {
    if (!actor.system.traits) return;
    for (let traitId of ["di", "dr", "dv", "sdi", "sdr", "sdv"]) {
      let trait = actor.system.traits[traitId];
      if (!trait) continue;
      if (!trait.value) trait.value = /* @__PURE__ */ new Set();
      for (let traitString of trait.value) {
        switch (traitString) {
          case "silver":
            trait.bypasses.add("sil");
            addPhysicalDamages(trait.value);
            break;
          case "adamant":
            trait.bypasses.add("adm");
            addPhysicalDamages(trait.value);
            break;
          case "physical":
            addPhysicalDamages(trait.value);
            break;
          case "nonmagic":
            addPhysicalDamages(trait.value);
            trait.bypasses.add("mgc");
            break;
          case "spell":
            break;
          // "power" type removed in v13
          case "magic":
            break;
          case "healing":
            break;
          case "temphp":
            break;
          default:
            trait.value.add(traitString);
        }
      }
    }
  } catch (err) {
    const message = `midi-qol | processTraits | error for ${actor?.name}`;
    console.warn(message, this, err);
    TroubleShooter.recordError(err, message);
  } finally {
  }
}
__name(processTraits, "processTraits");
function addPhysicalDamages(traitValue) {
  let physicalDamageTypes;
  physicalDamageTypes = Object.keys(GameSystemConfig.damageTypes).filter((dt) => GameSystemConfig.damageTypes[dt].isPhysical);
  for (let dt of physicalDamageTypes) {
    if (traitValue instanceof Set) traitValue.add(dt);
    else if (!traitValue.includes(dt)) traitValue.push(dt);
  }
}
__name(addPhysicalDamages, "addPhysicalDamages");
function actorGetRollData(wrapped, ...args) {
  const data = wrapped(...args);
  data.actorType = this.type;
  data.name = this.name;
  data.flags ??= {};
  data.flags["midi-qol"] = this.flags?.["midi-qol"] ?? {};
  data.midiFlags = data.flags["midi-qol"];
  data.cfg ??= {};
  data.cfg.armorClasses = GameSystemConfig.armorClasses;
  data.cfg.actorSizes = GameSystemConfig.actorSizes;
  data.cfg.skills = GameSystemConfig.skills;
  if (data.traits) {
    data.traits.sizeNumeric = GameSystemConfig.actorSizes[this.system.traits?.size]?.numerical ?? 2;
  }
  return data;
}
__name(actorGetRollData, "actorGetRollData");
function itemGetRollData(wrapped, ...args) {
  const data = wrapped(...args);
  if (!data) return data;
  data.item.flags = this.flags ?? {};
  data.item.flags["midi-qol"] = this.flags?.["midi-qol"] ?? {};
  data.item.midiFlags = data.item.flags["midi-qol"];
  data.item.name = this.name;
  data.item.itemType = this.type;
  return data;
}
__name(itemGetRollData, "itemGetRollData");
function _filterItems(wrapped, items, filters) {
  if (!filters.has("reaction")) return wrapped(items, filters);
  const revisedFilters = new Set(filters);
  revisedFilters.delete("reaction");
  let filteredItems = wrapped(items, revisedFilters);
  filteredItems = filteredItems.filter((item) => {
    if (item.system.activation?.type?.includes("reaction")) return true;
    return false;
  });
  return filteredItems;
}
__name(_filterItems, "_filterItems");

// src/module/apps/ActorOnUseMacroConfig.ts
var ApplicationV210 = foundry.applications.api.ApplicationV2;
var { HandlebarsApplicationMixin: HandlebarsApplicationMixin10 } = foundry.applications.api;
var ActorOnUseMacrosConfig = class extends HandlebarsApplicationMixin10(ApplicationV210) {
  static {
    __name(this, "ActorOnUseMacrosConfig");
  }
  document;
  constructor(options) {
    super(options);
    this.document = options.document;
  }
  // old "form" "active-effect-sheet" "sheet"
  static PARTS = {
    dialog: {
      id: "dialog-onuse-config",
      classes: ["dialog", "active-effect-sheet", "midi-qol"],
      template: "modules/midi-qol/templates/apps/ActorOnUseMacroConfig/actorOnUseMacrosConfig.hbs"
    }
  };
  // TODO: dragDrop? dropSelector .key
  static DEFAULT_OPTIONS = {
    window: {
      resizable: false,
      title: "midi-qol.ActorOnUseMacros"
    },
    position: {
      height: "auto",
      width: 550
    },
    form: {
      closeOnSubmit: false,
      submitOnClose: true
    },
    actions: {
      add: this.#onAddMacro,
      delete: this.#onDeleteMacro
    }
  };
  get isEditable() {
    if (this.document.pack) {
      const pack = game.packs?.get(this.document.pack);
      if (pack?.locked) return false;
    }
    return this.document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
  }
  async _prepareContext(options) {
    let context = await super._prepareContext(options);
    context.onUseMacroName = foundry.utils.getProperty(this.document._source, "flags.midi-qol.onUseMacroName");
    if (context.onUseMacroName !== void 0) context.onUseMacroParts = new OnUseMacros(context.onUseMacroName);
    else context.onUseMacroParts = new OnUseMacros(null);
    context.MacroPassOptions = foundry.utils.mergeObject(onUseMacroOptions, Workflow.stateHooks);
    Object.keys(context.MacroPassOptions).forEach((k) => {
      context.MacroPassOptions[k] = i18n(context.MacroPassOptions[k]);
    });
    return context;
  }
  _onDragStart(ev) {
  }
  _onDrop(ev) {
    ev.preventDefault();
    const data = foundry.applications.ux.TextEditor.getDragEventData(ev);
    if (data.uuid) ev.target.value += data.uuid;
  }
  static async #onAddMacro() {
    if (!this.isEditable) return;
    const macros = getCurrentSourceMacros(this.document);
    const formMacros = Object.entries(new foundry.applications.ux.FormDataExtended(this.element.querySelector("form"))?.object) ?? [];
    for (const [k, v] of formMacros) {
      foundry.utils.setProperty(macros, k.split(".").slice(1).join("."), v);
    }
    macros.items.push(new OnUseMacro(""));
    await this.document.setFlag(MODULE_ID, "onUseMacroName", macros.toString());
    this.render({ force: true });
  }
  static async #onDeleteMacro(event) {
    if (!this.isEditable) return;
    const macros = getCurrentSourceMacros(this.document);
    const formMacros = Object.entries(new foundry.applications.ux.FormDataExtended(this.element.querySelector("form"))?.object) ?? [];
    for (const [k, v] of formMacros) {
      foundry.utils.setProperty(macros, k.split(".").slice(1).join("."), v);
    }
    const li = event.target.closest("[data-macroPart]");
    macros.items.splice(Number(li.dataset.macropart), 1);
    await this.document.setFlag(MODULE_ID, "onUseMacroName", macros.toString());
    this.render({ force: true });
  }
  async _preClose(options) {
    await super._preClose(options);
    const macros = getCurrentSourceMacros(this.document);
    const formMacros = Object.entries(new foundry.applications.ux.FormDataExtended(this.element.querySelector("form"))?.object) ?? [];
    for (const [k, v] of formMacros) {
      foundry.utils.setProperty(macros, k.split(".").slice(1).join("."), v);
    }
    await this.document.setFlag(MODULE_ID, "onUseMacroName", macros.toString());
  }
};

// src/module/apps/ItemOnUseMacrosConfig.ts
var { DocumentSheetV2, HandlebarsApplicationMixin: HandlebarsApplicationMixin11 } = foundry.applications.api;
var ItemOnUseMacrosConfig = class extends HandlebarsApplicationMixin11(DocumentSheetV2) {
  static {
    __name(this, "ItemOnUseMacrosConfig");
  }
  static PARTS = {
    form: {
      id: "item-on-use-config",
      classes: ["midi-qol", "item-on-use-sheet", "standard-form"],
      template: "modules/midi-qol/templates/apps/ItemOnUseMacrosConfig/itemOnUseMacrosConfig.hbs"
    }
  };
  static DEFAULT_OPTIONS = {
    form: {
      handler: this.#onSubmit,
      submitOnChange: true,
      closeOnSubmit: false
    },
    position: {
      width: 600
    },
    tag: "form",
    actions: {
      addMacro: this.#onAddMacro,
      deleteMacro: this.#onDeleteMacro,
      editMacro: this.#onEditMacro
    }
  };
  /*
  New way to store on use macros, maybe:
  flags.midi-qol.onUseMacros: [
    {
      macro: "ItemMacro" or "ActivityMacro-ActivityId" or "Custom"
      customMacro: "Some Macro" or nullish
      macroPass: "the selected one of above"
    }
  ]
  */
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    const activityMacros = this.document.system.activities.reduce((acc, activity) => ({
      ...acc,
      [`ActivityMacro-${activity.id}`]: `${activity.name} DIME`
    }), {});
    const macros = {
      ItemMacro: `${i18n("DOCUMENT.Item")} DIME`,
      ...activityMacros,
      Custom: i18n("midi-qol.CustomMacro")
    };
    const onUseMacros = foundry.utils.getProperty(this.document, "flags.midi-qol.onUseMacroParts")?.items ?? [];
    for (const currMacro of onUseMacros) {
      if (currMacro.macroName.startsWith("ActivityMacro-")) {
        currMacro.macro = currMacro.macroName;
        const activityId = currMacro.macroName.split("-").slice(1).join("-");
        const activityType = this.document.system.activities.get(activityId)?.type;
        currMacro.macroPassOptions = Workflow.allMacroPasses;
      } else {
        currMacro.macroPassOptions = Workflow.allMacroPasses;
        if (currMacro.macroName === "ItemMacro") currMacro.macro = currMacro.macroName;
        else currMacro.macro = "Custom";
      }
    }
    foundry.utils.mergeObject(context, {
      onUseMacros,
      macros
    });
    return context;
  }
  static #onSubmit(_event, _form, formData) {
    const expandedData = foundry.utils.expandObject(formData.object);
    if (!expandedData) return void this.document.update({ "flags.midi-qol.onUseMacroName": "" });
    const onUseMacros = Object.values(expandedData.onUseMacros);
    onUseMacros.forEach((i) => i.macroName ??= i.macro);
    const newOnUseMacros = OnUseMacros.parseParts({ items: onUseMacros });
    this.document.update({ "flags.midi-qol.onUseMacroName": newOnUseMacros.toString() });
  }
  static #onAddMacro(_event, _target) {
    const currOnUseMacros = foundry.utils.getProperty(this.document, "flags.midi-qol.onUseMacroParts") ?? new OnUseMacros();
    currOnUseMacros.items.push(new OnUseMacro());
    this.document.update({ "flags.midi-qol.onUseMacroName": currOnUseMacros.toString() });
  }
  static #onDeleteMacro(_event, target) {
    const currOnUseMacros = foundry.utils.getProperty(this.document, "flags.midi-qol.onUseMacroParts")?.items ?? [];
    const macroIndex = target.closest("[data-macro-index]")?.dataset.macroIndex;
    if (macroIndex === void 0) return;
    const newOnUseMacros = OnUseMacros.parseParts({ items: currOnUseMacros.toSpliced(Number(macroIndex), 1) });
    this.document.update({ "flags.midi-qol.onUseMacroName": newOnUseMacros.toString() });
  }
  static #onEditMacro(_event, target) {
    const macroIndex = target.closest("[data-macro-index]")?.dataset.macroIndex;
    if (macroIndex === void 0) return;
    const currOnUse = foundry.utils.getProperty(this.document, "flags.midi-qol.onUseMacroParts")?.items?.[macroIndex];
    if (!currOnUse) return;
    const DIMEditor = globalThis.DAE?.DIMEditor;
    if (!DIMEditor) return ui.notifications?.error("DIMEditor not available - install Dynamic Active Effects");
    if (currOnUse.macroName === "ItemMacro") {
      return new DIMEditor({ document: this.document }).render({ force: true });
    } else {
      const activityId = currOnUse.macroName.split("-").slice(1).join("-");
      const activity = this.document.system.activities.get(activityId);
      if (activity) return new DIMEditor({ document: activity }).render({ force: true });
    }
  }
};

// src/module/Hooks.ts
var concentrationCheckItemDisplayName = "Concentration Check";
var readyHooks = /* @__PURE__ */ __name(async () => {
  Hooks.on("moveToken", (tokenDocument, movement, operation, user) => {
    if (!user.isSelf) return;
    const actor = tokenDocument.actor;
    const expiredEffects = actor?.effects.filter((ef) => {
      const specialDuration = ef.flags?.dae?.specialDuration;
      return !!specialDuration?.includes("isMoved");
    }) ?? [];
    if (expiredEffects.length > 0) expireEffects(actor, expiredEffects, { "expiry-reason": "midi-qol:isMoved" });
  });
  Hooks.on("template3dUpdatePreview", (at, t) => {
    globalThis.VolumetricTemplates?.compute3Dtemplate(t);
  });
  Hooks.on("targetToken", foundry.utils.debounce(chackFlanking, 150));
  Hooks.on("targetToken", (user, token, targeted) => onUserManualTarget(user));
  const debouncedFlankingHighlight = foundry.utils.debounce(updateFlankingHighlight, 150);
  Hooks.on("controlToken", debouncedFlankingHighlight);
  Hooks.on("targetToken", debouncedFlankingHighlight);
  Hooks.on("refreshToken", debouncedFlankingHighlight);
  Hooks.on("ddb-game-log.pendingRoll", (data) => {
    ddbglPendingHook(data);
  });
  Hooks.on("ddb-game-log.fulfilledRoll", (data) => {
    ddbglPendingHook(data);
  });
  Hooks.on("preUpdateChatMessage", (message, update, options, user) => {
    try {
      const cachedUpdates = foundry.utils.flattenObject(getUpdatesCache(message.uuid));
      if (cachedUpdates) {
        if (!foundry.utils.isEmpty(cachedUpdates) && false) {
          const insertedUpdates = {};
          Object.keys(cachedUpdates).forEach((key) => {
            if (cachedUpdates[key] !== void 0 && foundry.utils.getProperty(update, key) === void 0) {
              if (debugEnabled) insertedUpdates[key] = cachedUpdates[key];
              foundry.utils.setProperty(update, key, cachedUpdates[key]);
            }
          });
          if (debugEnabled && !foundry.utils.isEmpty(insertedUpdates)) warn("preUpdateChatMessage inserting updates", message.uuid, insertedUpdates);
        }
      }
      return true;
    } finally {
      return true;
    }
  });
  Hooks.on("deleteMeasuredTemplate", checkDeleteTemplate);
  Hooks.on("refreshMeasuredTemplate", (template, flags) => {
    if (!flags.refreshPosition && !flags.refreshShape) return;
    if (!canvas?.tokens) return;
    if (template.document.author?.id !== game.user?.id) return;
    if (!template.activity && !template.document.flags?.dnd5e?.origin) return;
    const autoTarget = getActivityAutoTargetAction2(template.activity);
    if (autoTarget === "none") return;
    if (installedModules.get("levelsvolumetrictemplates") && autoTarget !== "walledtemplates") {
      const levelsTemplateData = CONFIG.Levels.handlers.TemplateHandler.getTemplateData(false);
      if (template.document.elevation !== levelsTemplateData.elevation) {
        template.document.elevation = levelsTemplateData.elevation;
      }
      const activity = template.activity ?? (template.document.flags?.dnd5e?.origin ? fromUuidSync(template.document.flags.dnd5e.origin) : void 0);
      const sourceToken = getTemplateSourceToken(template.document);
      const selfToken = sourceToken ?? void 0;
      const AoETargetType = activity ? getAoETargetType2(activity) : "any";
      const ignoreSelf = activity ? (activity?.target.affects.special ?? "").split(";").some((spec) => ["-self"].includes(spec)) : false;
      let distance = template.document.distance;
      const dimensions = canvas.dimensions || { size: 1, distance: 1 };
      distance *= dimensions.size / dimensions.distance;
      const tokensToCheck = canvas.tokens.placeables?.filter((tk) => {
        const r = new foundry.canvas.geometry.Ray(
          { x: template.document?.x ?? template.x, y: template.document?.y ?? template.y },
          { x: tk.x + tk.document.width * dimensions.size, y: tk.y + tk.document.height * dimensions.size }
        );
        const maxExtension = (1 + Math.max(tk.document.width, tk.document.height)) * dimensions.size;
        if (r.distance > distance + maxExtension) return false;
        if (!isAoETargetable(tk, { selfToken, ignoreSelf, AoETargetType, autoTarget })) return false;
        return true;
      });
      if (tokensToCheck && tokensToCheck.length > 0) {
        VolumetricTemplates.compute3Dtemplate(template, tokensToCheck);
      }
    } else {
      const activity = template.activity ?? (template.document.flags?.dnd5e?.origin ? fromUuidSync(template.document.flags.dnd5e.origin) : void 0);
      const sourceToken = getTemplateSourceToken(template.document);
      if (activity && sourceToken) {
        const ignoreSelf = (activity?.target.affects.special ?? "").split(";").some((spec) => ["-self"].includes(spec));
        selectTargetsFromTemplates([template], sourceToken, ignoreSelf, getAoETargetType2(activity), autoTarget);
      } else {
        selectTargetsFromTemplates([template]);
      }
    }
  });
  Hooks.on("updateActor", async (actor, update, options, user) => {
    if (user !== game.user?.id) return;
    const hpUpdate = foundry.utils.getProperty(update, "system.attributes.hp.value");
    const temphpUpdate = foundry.utils.getProperty(update, "system.attributes.hp.temp");
    if (hpUpdate !== void 0 || temphpUpdate !== void 0) {
      await zeroHPExpiry(actor, update, options, user);
      if (actor.system.attributes.hp.value <= 0 && configSettings.removeConcentration) {
        await actor.endConcentration();
      }
      return;
    }
  });
  Hooks.on("updateItem", async (item, update, options, userId) => {
    if (userId !== game.user?.id) return;
    const spentUpdate = foundry.utils.getProperty(update, "system.uses.spent");
    if (spentUpdate === void 0) return;
    const actor = item.actor;
    if (!actor) return;
    const vitalityItem = getVitalityItem(actor);
    if (!vitalityItem || vitalityItem.id !== item.id) return;
    const maxUses = item.system.uses?.max ?? 0;
    const newVitality = maxUses - spentUpdate;
    await checkWounded(actor, {}, {}, userId, newVitality);
  });
  Hooks.on("preUpdateActor", (actor, changes, options, userId) => {
    if (userId !== game.user?.id) return true;
    const shouldCheckTempHP = configSettings.tempHPDamageConcentrationCheck;
    const relevantHpChange = changes.system?.attributes?.hp;
    const isHpChangeInvalid = relevantHpChange && !("value" in relevantHpChange) && (!shouldCheckTempHP || !("temp" in relevantHpChange));
    if (isHpChangeInvalid || // @ts-expect-error no dnd5e-types
    !actor.concentration.effects.size || configSettings.doConcentrationCheck === "none" || game.settings.get("dnd5e", "disableConcentration") || options.noConcentrationCheck || options.dnd5e?.concentrationCheck === false) {
      options.noConcentrationCheck = true;
      foundry.utils.setProperty(options, "dnd5e.concentrationCheck", false);
      return true;
    }
    const hpUpdate = relevantHpChange?.value;
    const tempHpUpdate = relevantHpChange?.temp;
    const hpDamage = hpUpdate !== void 0 ? actor.system.attributes.hp.value - hpUpdate : 0;
    const tempHPDamage = tempHpUpdate !== void 0 ? actor.system.attributes.hp.temp - tempHpUpdate : 0;
    const totalDamage = tempHPDamage + hpDamage;
    if (hpDamage <= 0 && !configSettings.tempHPDamageConcentrationCheck) {
      options.noConcentrationCheck = true;
      foundry.utils.setProperty(options, "dnd5e.concentrationCheck", false);
      return true;
    }
    const isConcentrationCheck = hpDamage > 0 || tempHPDamage > 0 && configSettings.tempHPDamageConcentrationCheck;
    foundry.utils.setProperty(options, "dnd5e.concentrationCheck", isConcentrationCheck);
    try {
      if (configSettings.doConcentrationCheck === "item" && isConcentrationCheck) {
        const concentrationDc = actor.getConcentrationDC(totalDamage);
        doConcentrationCheck(actor, concentrationDc);
      }
    } catch (err) {
      error2("Error in preUpdateActor", err);
    } finally {
      return true;
    }
  });
  Hooks.on("renderActorArmorConfig", (app, html, data) => {
    if (!["none", void 0, false].includes(checkRule("challengeModeArmor"))) {
      const ac = data.ac;
      const element = html.querySelector(".card");
      const arTemplate = document.createElement("template");
      arTemplate.innerHTML = `<div>EC: ${ac.EC}</div><div>AR: ${ac.AR}</div>`;
      element?.append(...Array.from(arTemplate.content.children));
    }
  });
  Hooks.on("deleteActiveEffect", (deletedEffect, options, user) => {
    if (options.undo) return;
    if (debugEnabled > 0) console.warn("deleteActiveEffect", deletedEffect, options, user);
    if (!preferredActiveGM()?.isSelf) return;
    if (!(deletedEffect.parent instanceof CONFIG.Actor.documentClass)) return;
    if (debugEnabled > 0) warn("deleteActiveEffectHook", deletedEffect, deletedEffect.parent.name, options);
    async function changeFunc() {
      try {
        let origin = fromUuidSync(deletedEffect.origin);
        if (origin instanceof ActiveEffect && !options.noConcentrationCheck && configSettings.removeConcentrationEffects !== "none") {
          if (origin.statuses?.has(CONFIG.specialStatusEffects.CONCENTRATING) && origin.getDependents()?.length === 0) {
            if (!installedModules.get("times-up") || (origin?.duration?.remaining ?? 1) > 0) {
              await origin.delete();
            }
          }
        }
        let transformedActorUuids = foundry.utils.getProperty(deletedEffect, "flags.midi-qol.transformedActorUuids");
        const parentActor = deletedEffect.parent;
        const parentStillExists = parentActor?.id && (parentActor.isToken ? canvas.scene?.tokens?.get(parentActor.token?.id ?? "") : game.actors?.get(parentActor.id));
        if (transformedActorUuids && transformedActorUuids.length > 0 && parentStillExists) {
          for (let uuid of transformedActorUuids) {
            let originalActor = await fromUuid(uuid);
            if (!originalActor) continue;
            const actorsToRevert = [];
            for (const a of game.actors ?? []) {
              if (a.isPolymorphed && a.getFlag("dnd5e", "originalActor") === originalActor.id) {
                actorsToRevert.push(a);
              }
            }
            if (canvas.ready) {
              for (const token of canvas.scene?.tokens ?? []) {
                if (token.actor?.isPolymorphed && token.actor?.getFlag("dnd5e", "originalActor") === originalActor.id && !actorsToRevert.some((a) => a.id === token.actor.id)) {
                  actorsToRevert.push(token.actor);
                }
              }
            }
            for (const polyActor of actorsToRevert) {
              try {
                await polyActor.revertOriginalForm({});
              } catch (err) {
                console.error("Error reverting actor to original form", polyActor, err);
              }
            }
            if (actorsToRevert.length > 0) {
              const restoredActor = await fromUuid(uuid);
              if (restoredActor) {
                const remainingConcentration = restoredActor.effects.filter(
                  (ef) => (
                    // @ts-expect-error no dnd5e-types
                    ef.statuses?.has(CONFIG.specialStatusEffects.CONCENTRATING)
                  )
                );
                for (const ce of remainingConcentration) {
                  await ce.delete({ noConcentrationCheck: true });
                }
              }
            }
          }
        }
        return true;
      } catch (err) {
        console.warn("Error in deleteActiveEffect", err, deletedEffect, options);
        return true;
      }
    }
    __name(changeFunc, "changeFunc");
    return changeFunc();
  });
  Hooks.on("deleteActor", (deletedActor, options, user) => {
    if (!preferredActiveGM()?.isSelf) return;
    if (options.undo) return;
    MidiDependentsRegistry.deleteDependents(deletedActor.uuid);
  });
  Hooks.on("deleteItem", (deletedItem, options, user) => {
    if (!preferredActiveGM()?.isSelf) return;
    if (options.undo) return;
    MidiDependentsRegistry.deleteDependents(deletedItem.uuid);
  });
  Hooks.on("deleteAmbientLight", (deletedLight, options, user) => {
    if (!preferredActiveGM()?.isSelf) return;
    if (options.undo) return;
    MidiDependentsRegistry.deleteDependents(deletedLight.uuid);
  });
  Hooks.on("deleteToken", (deletedToken, options, user) => {
    if (!preferredActiveGM()?.isSelf) return;
    if (options.undo) return;
    MidiDependentsRegistry.deleteDependents(deletedToken.uuid);
  });
  Hooks.on("deleteRegion", (deletedRegion, options, user) => {
    if (!preferredActiveGM()?.isSelf) return;
    if (options.undo) return;
    MidiDependentsRegistry.deleteDependents(deletedRegion.uuid);
  });
  Hooks.on("dnd5e.restCompleted", restManager);
  Hooks.on("dnd5e.preActivityConsumption", preActivityConsumptionHook);
  Hooks.on("dnd5e.activityConsumption", activityConsumptionHook);
  Hooks.on("dnd5e.rollDeathSave", deathSaveHook);
  Hooks.on("updateCombat", (combat, update, options, userId) => {
    if (userId != preferredActiveGM()?.id) return;
    if (!update.hasOwnProperty("round")) return;
    if (!checkMechanic("autoRerollInitiative")) return;
    let combatantIds = combat.combatants.map((c) => c.id);
    if (combat.combatants?.size > 0 && combat.id) {
      const combatId = combat.id;
      combat.rollInitiative(combatantIds, { updateTurn: true }).then(() => {
        if (game.combats?.get(combatId)) combat.update({ turn: 0 });
      }).catch((err) => TroubleShooter.recordError(err, "autoRerollInitiative"));
    }
  });
}, "readyHooks");
async function restManager(actor, result) {
  if (!actor || !result)
    return;
  const specialDuration = /* @__PURE__ */ __name((effect) => {
    return foundry.utils.getProperty(effect, "flags.dae.specialDuration");
  }, "specialDuration");
  const effectsToExpire = /* @__PURE__ */ __name((actorRef) => {
    const effects = actorRef.appliedEffects;
    const validEffects = effects.filter((effect) => (specialDuration(effect) ?? []).length > 0);
    return {
      newDay: validEffects.filter((ef) => result.newDay && specialDuration(ef)?.includes(`newDay`)),
      longRest: validEffects.filter((ef) => result.longRest && specialDuration(ef)?.includes(`longRest`) && !specialDuration(ef)?.includes(`newDay`)),
      shortRest: validEffects.filter((ef) => specialDuration(ef)?.includes(`shortRest`) && !specialDuration(ef)?.includes(`newDay`))
    };
  }, "effectsToExpire");
  const myExpiredEffects = effectsToExpire(actor);
  if (result.longRest && myExpiredEffects.longRest.length) await expireEffects(actor, myExpiredEffects.longRest, { "expiry-reason": "midi-qol:longRest" });
  if (result.longRest && myExpiredEffects.shortRest.length) await expireEffects(actor, myExpiredEffects.shortRest, { "expiry-reason": "midi-qol:shortRest" });
  if (!result.longRest && myExpiredEffects.shortRest.length) await expireEffects(actor, myExpiredEffects.shortRest, { "expiry-reason": "midi-qol:shortRest" });
  if (result.newDay && myExpiredEffects.newDay.length) await expireEffects(actor, myExpiredEffects.newDay, { "expiry-reason": "midi-qol:newDay" });
  if (foundry.utils.getProperty(actor, "flags.midi-qol.actions.reactionsReset") !== "never") await removeReactionUsed(actor, true);
  if (foundry.utils.getProperty(actor, "flags.midi-qol.actions.bonusActionsReset") !== "never")
    await removeActionUsed(actor);
}
__name(restManager, "restManager");
function initHooks() {
  if (debugEnabled > 0) warn("Init Hooks processing");
  Hooks.on("preCreateChatMessage", (message, data, options, user) => {
    if (debugEnabled > 1) debug("preCreateChatMessage entering", message, data, options, user);
    nsaMessageHandler(message, data, options, user);
    checkOverTimeSaves(message, data, options, user);
    return true;
  });
  Hooks.on("createChatMessage", (message, options, user) => {
    if (debugEnabled > 1) debug("Create Chat Message ", message.id, message, options, user);
    processCreateDDBGLMessages(message, options, user);
    return true;
  });
  Hooks.on("updateChatMessage", (message, update, options, user) => {
    hideRollUpdate(message, update, options, user);
    if (!update.content) return;
    const updateChatMessageStart = Date.now();
    if (message.content && ui.chat?.isAtBottom) {
      throttledScrollBottom();
    }
    if (debugEnabled > 1) debug("updateChatMessage processing time", Date.now() - updateChatMessageStart);
  });
  Hooks.on("updateCombat", (combat, data, options, user) => {
    if (data.round === void 0 && data.turn === void 0) return;
    untargetAllTokens(combat);
    untargetDeadTokens();
    if (preferredActiveGM()?.isSelf) {
      _processOverTime(combat, data, options, user);
      _processActivityOverTime(combat, data, options, user);
    }
  });
  Hooks.on("renderChatMessageHTML", (message, html, data) => {
    if (debugEnabled > 1) debug("render message hook ", message.id, message, html, data);
    processUndoDamageCard(message, html, data);
    colorChatMessageHandler(message, html, data);
    hideRollRender(message, html, data);
    hideStuffHandler(message, html, data);
    processConcentrationRequestMessage(message, html, data);
  });
  Hooks.on("deleteChatMessage", (message, options, user) => {
    Workflow.deleteWorkflow(message.uuid);
    clearUpdatesCache(message.uuid);
  });
  async function applyReplacementEffect(actor, effectSpec, effect) {
    const parts = effectSpec.split("|");
    const effectIdOrUuid = parts[0].trim();
    let overlay = false;
    for (let i = 1; i < parts.length; i++) {
      const param = parts[i].trim().toLowerCase();
      if (param === "overlay" || param === "overlay=true") {
        overlay = true;
      } else if (param === "overlay=false") {
        overlay = false;
      }
    }
    if (effectIdOrUuid.startsWith("function.") || effectIdOrUuid.startsWith("Macro.") || effectIdOrUuid.startsWith("ItemMacro") || effectIdOrUuid.startsWith("ActivityMacro")) {
      await executeMacroSpec(effectIdOrUuid, actor, effect);
    } else {
      const statusEffect = CONFIG.statusEffects.find((se) => se.id === effectIdOrUuid);
      if (statusEffect) {
        await actor.toggleStatusEffect(effectIdOrUuid, { active: true, overlay });
      } else {
        const sourceEffect = await fromUuid(effectIdOrUuid);
        if (sourceEffect) {
          const effectData = sourceEffect.toObject();
          if (overlay) {
            foundry.utils.setProperty(effectData, "flags.core.overlay", true);
          }
          await actor.createEmbeddedDocuments("ActiveEffect", [effectData]);
        }
      }
    }
  }
  __name(applyReplacementEffect, "applyReplacementEffect");
  async function executeMacroSpec(macroSpec, actor, effect) {
    let macro;
    let macroData;
    let macroItem;
    let macroActivity;
    let originItem;
    let originActivity;
    if (effect?.origin) {
      const origin = await fromUuid(effect.origin);
      if (origin instanceof Item) {
        originItem = origin;
        originActivity = origin.system.activities?.contents[0];
      } else if (origin?.item) {
        originActivity = origin;
        originItem = origin.item;
      }
    }
    try {
      if (macroSpec.startsWith("function.")) {
        const funcName = macroSpec.replace("function.", "").trim();
        macroData = {
          name: "replacement effect function call",
          type: "script",
          command: `return await ${funcName}({ speaker, actor, token, character, item, args, scope })`
        };
        macroItem = originItem;
      } else if (macroSpec.startsWith("Macro.")) {
        const macroName = macroSpec.replace("Macro.", "").trim();
        macro = game.macros?.getName(macroName);
        if (!macro) {
          const macroEntity = await fromUuid(macroName);
          if (macroEntity instanceof Macro) macro = macroEntity;
        }
        if (!macro) {
          warn(`applyReplacementEffect | Could not find macro ${macroName}`);
          return;
        }
        if (macro.type === "chat") {
          macro.execute();
          return;
        }
        macroItem = originItem;
      } else if (macroSpec.startsWith("ItemMacro")) {
        const resolved = await resolveItemMacro(macroSpec, { actor, originItem });
        if (resolved) {
          macroItem = resolved.item;
          macroData = resolved.macroData;
        } else if (effect?.origin) {
          const itemData = resolveDAEItemData(actor, effect.origin);
          if (itemData) {
            macroItem = itemData;
            macroData = itemData.flags?.dae?.macro ?? itemData.flags?.itemacro?.macro;
          }
        }
        if (!macroData) {
          warn(`applyReplacementEffect | Could not resolve ItemMacro: ${macroSpec}`);
          return;
        }
      } else if (macroSpec.startsWith("ActivityMacro")) {
        const resolved = await resolveActivityMacro(macroSpec, { originItem, originActivity });
        if (!resolved) {
          warn(`applyReplacementEffect | Could not resolve ActivityMacro: ${macroSpec}`);
          return;
        }
        macroItem = resolved.item;
        macroActivity = resolved.activity;
        macro = resolved.macro;
      }
      if (macroData && !macro) {
        const OWNER = CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER;
        macroData.ownership = { default: OWNER };
        macroData.author = game.user?.id;
        macro = new CONFIG.Macro.documentClass(macroData);
      }
      if (!macro?.command) return;
      const token = tokenForActor(actor);
      const speaker = ChatMessage.getSpeaker({ actor, token: token?.document });
      const args = [{ actor, token, effect, item: macroItem, activity: macroActivity, reason: "replacementEffect" }];
      const scope = {};
      scope.actor = actor;
      scope.token = token;
      scope.effect = effect;
      scope.item = macroItem;
      scope.activity = macroActivity;
      scope.args = args;
      return macro.execute(scope);
    } catch (err) {
      TroubleShooter.recordError(err, "executeMacroSpec: Error evaluating macro");
      error2("Error evaluating replacement effect macro", err);
    }
  }
  __name(executeMacroSpec, "executeMacroSpec");
  Hooks.on("midi-qol.RollComplete", async (workflow) => {
    const activityUuid = workflow.activity.uuid ?? "";
    if (savedOverTimeEffectsToDelete[activityUuid]) {
      const effectData = savedOverTimeEffectsToDelete[activityUuid];
      let effect = fromUuidSync(effectData.uuid);
      if (effect) {
        const saved = workflow.saves.size === 1;
        const failed = workflow.failedSaves.size === 1;
        const {
          saveCount,
          saveCountAction,
          saveConsecutive,
          failCount,
          failCountAction,
          failConsecutive
        } = effectData;
        const actor = effect.parent;
        if (saved || !workflow.hasSave) {
          if (saved && saveCount !== void 0) {
            let savesRemaining = foundry.utils.getProperty(effect, `flags.${MODULE_ID}.overtime.savesRemaining`);
            if (savesRemaining === void 0) savesRemaining = saveCount;
            savesRemaining -= 1;
            if (savesRemaining <= 0) {
              const action = (saveCountAction ?? "-").replace(/^c/, "");
              const isRemove = action.startsWith("-");
              const effectSpec = action.length > 1 ? action.slice(1) : void 0;
              if (effectSpec) await applyReplacementEffect(actor, effectSpec, effect);
              if (isRemove) {
                expireEffects(actor, [effect], { "expiry-reason": "midi-qol:overTime:saveCount" });
              } else {
                const permanentUpdate = { [`flags.${MODULE_ID}.overtime.permanent`]: true };
                await effect.update(permanentUpdate);
              }
            } else {
              const updates = {
                [`flags.${MODULE_ID}.overtime.savesRemaining`]: savesRemaining
              };
              if (failConsecutive && failCount !== void 0) {
                updates[`flags.${MODULE_ID}.overtime.failsRemaining`] = failCount;
              }
              await effect.update(updates);
            }
          } else {
            const isAlreadyPermanent = !!effect.flags?.[MODULE_ID]?.overtime?.permanent;
            if (!isAlreadyPermanent) {
              expireEffects(actor, [effect], { "expiry-reason": "midi-qol:overTime" });
            }
          }
        } else if (failed && failCount !== void 0) {
          let failsRemaining = foundry.utils.getProperty(effect, `flags.${MODULE_ID}.overtime.failsRemaining`);
          if (failsRemaining === void 0) failsRemaining = failCount;
          failsRemaining -= 1;
          if (failsRemaining <= 0) {
            const action = (failCountAction ?? "+").replace(/^c/, "");
            const isRemove = action.startsWith("-");
            const effectSpec = action.length > 1 ? action.slice(1) : void 0;
            if (effectSpec) await applyReplacementEffect(actor, effectSpec, effect);
            if (isRemove) {
              expireEffects(actor, [effect], { "expiry-reason": "midi-qol:overTime:failCount" });
            } else {
              const permanentUpdate = { [`flags.${MODULE_ID}.overtime.permanent`]: true };
              await effect.update(permanentUpdate);
            }
          } else {
            const updates = {
              [`flags.${MODULE_ID}.overtime.failsRemaining`]: failsRemaining
            };
            if (saveConsecutive && saveCount !== void 0) {
              updates[`flags.${MODULE_ID}.overtime.savesRemaining`] = saveCount;
            }
            await effect.update(updates);
          }
        } else if (failed && saveConsecutive && saveCount !== void 0) {
          const resetUpdate = { [`flags.${MODULE_ID}.overtime.savesRemaining`]: saveCount };
          await effect.update(resetUpdate);
        }
      }
      delete savedOverTimeEffectsToDelete[activityUuid];
    }
    if (overTimeEffectsToDelete[activityUuid]) {
      let effect = fromUuidSync(overTimeEffectsToDelete[activityUuid].uuid);
      if (effect) expireEffects(effect.parent, [effect], { "expiry-reason": "midi-qol:overTime" });
      delete overTimeEffectsToDelete[activityUuid];
    }
    if (debugEnabled > 1) debug("Finished the roll", activityUuid, workflow.id);
  });
  Hooks.on("applyActiveEffect", midiCustomEffect);
  Hooks.on("preUpdateItem", preUpdateItemActorOnUseMacro);
  Hooks.on("preUpdateActor", preUpdateItemActorOnUseMacro);
  Hooks.on("combatRound", expirePerTurnBonusActions);
  Hooks.on("combatTurn", expirePerTurnBonusActions);
  Hooks.on("updateCombatant", (combatant, updates, options, user) => {
    if (game.user?.id !== user) return true;
    if (combatant.actor && updates.initiative) expireRollEffect.bind(combatant.actor)("Initiative", "none");
    return true;
  });
  Hooks.on("preCreateChatMessage", (message, messageData, options, userId) => {
    if (!message.system?.activations) return true;
    if (configSettings.activationAutomation === "chat") return true;
    if (configSettings.activationAutomation === "none") {
      message.updateSource({ "system.activations": [] });
      if (message.content?.length === 0) return false;
      return true;
    }
    try {
      const activations = /* @__PURE__ */ new Set();
      const activityList = [];
      const actor = message.system.actor;
      if (message?.system?.activations?.size > 0) {
        for (let activation of message.system.activations) {
          const activity = fromUuidSync(activation, { relative: actor });
          const item = activity.item;
          const itemEnabled = item?.system.equipped !== false && (item?.system.attuned === true || item?.system.attunement !== "required");
          if (itemEnabled && activity?.target?.affects.type === "self") activityList.push(activity);
          else if (itemEnabled) activations.add(activation);
        }
      }
      message.updateSource({ "system.activations": activations });
      const processActivations = /* @__PURE__ */ __name(async () => {
        for (let activity of activityList) {
          await completeActivityUse2(activity, { midiOptions: { noUseWarning: true } }, {}, {});
        }
      }, "processActivations");
      processActivations();
    } catch (err) {
      const errMessage = "Activation Automation | preCreateChatMessage error";
      error2(errMessage, err);
      TroubleShooter.recordError(err, errMessage);
    } finally {
      return message.content.length === 0 && message.system.activations.size === 0 ? false : true;
    }
  });
  Hooks.once("tidy5e-sheet.ready", (api) => {
    Hooks.on("tidy5e-sheet.getActivitiesForPlay", (parent, data) => {
      if (data.activities && data.activities instanceof Array) {
        data.activities = data.activities.filter(
          (activity) => activity?.canUse !== false && !activity?.midiProperties?.automationOnly && !parent.flags.dnd5e?.riders?.activity?.includes(activity.id)
        );
      }
    });
    api.config.itemSummary.registerCommands([
      {
        label: i18n("midi-qol.buttons.showChatInfo"),
        enabled: /* @__PURE__ */ __name((params) => true, "enabled"),
        iconClass: "fas fa-info-circle",
        execute: /* @__PURE__ */ __name((params) => {
          if (debugEnabled > 1) log("displayCard", params.item);
          params.item.displayCard();
        }, "execute")
      },
      {
        label: i18n("midi-qol.buttons.roll"),
        enabled: /* @__PURE__ */ __name((params) => ["weapon", "spell", "feat", "tool", "consumable"].includes(params.item.type), "enabled"),
        iconClass: "fas fa-dice-d20",
        execute: /* @__PURE__ */ __name((params) => {
          if (debugEnabled > 1) log("roll", params.item);
          Workflow.removeWorkflow(params.item.uuid);
          params.item.use({ legacy: false, event: params.event, configureDialog: true, systemCard: true }, {}, { systemCard: true });
        }, "execute")
      },
      {
        label: i18n("midi-qol.buttons.attack"),
        enabled: /* @__PURE__ */ __name((params) => params.item.system.activities?.some((a) => a.hasAttack), "enabled"),
        iconClass: "fas fa-dice-d20",
        execute: /* @__PURE__ */ __name((params) => {
          if (debugEnabled > 1) log("rollAttack", params.item);
          const activity = params.item.system.activities.find((a) => a.hasAttack);
          if (!activity) return;
          let configure = !isAutoFastForward2("attack");
          if (areMidiKeysPressed(params.event, "RollToggle")) configure = !configure;
          activity.rollAttack({ event: params.event }, { configure }, { create: true });
        }, "execute")
      },
      {
        label: i18n("midi-qol.buttons.damage"),
        enabled: /* @__PURE__ */ __name((params) => params.item.system.activities?.some((a) => a.hasDamage), "enabled"),
        iconClass: "fas fa-dice-d20",
        execute: /* @__PURE__ */ __name((params) => {
          if (debugEnabled > 1) log("rollDamage", params.item);
          const activity = params.item.system.activities.find((a) => a.hasDamage);
          if (!activity) return;
          let configure = !isAutoFastForward2("damage");
          if (areMidiKeysPressed(params.event, "RollToggle")) configure = !configure;
          activity.rollDamage({ event: params.event }, { configure }, { create: true });
        }, "execute")
      },
      {
        label: i18n("midi-qol.buttons.versatileDamage"),
        enabled: /* @__PURE__ */ __name((params) => params.item.isVersatile && params.item.system.activities?.some((a) => a.hasDamage), "enabled"),
        iconClass: "fas fa-hand-fist",
        execute: /* @__PURE__ */ __name((params) => {
          if (debugEnabled > 1) log("rollVersatileDamage", params.item);
          const activity = params.item.system.activities.find((a) => a.hasDamage);
          if (!activity) return;
          let configure = !isAutoFastForward2("damage");
          if (areMidiKeysPressed(params.event, "RollToggle")) configure = !configure;
          activity.rollDamage({ event: params.event, versatile: true }, { configure }, { create: true });
        }, "execute")
      }
    ]);
    return;
  });
  Hooks.once("tidy5e-sheet.ready", (api) => {
    if ((game.user?.role ?? CONST.USER_ROLES.PLAYER) < (configSettings.midiPropertiesTabRole ?? CONST.USER_ROLES.PLAYER)) return;
    api.registerItemHeaderControls?.({
      controls: [
        {
          icon: "fa-solid fa-gears",
          // TODO: better icon?
          label: "Midi-QOL",
          async onClickAction() {
            new ItemOnUseMacrosConfig({ document: this.document }).render({ force: true });
          }
        }
      ]
    });
  });
  Hooks.on("getHeaderControlsActorSheetV2", (app, controls) => {
    if (configSettings.allowActorUseMacro) {
      controls.push({
        label: i18n("midi-qol.ActorOnUseMacros"),
        icon: "fa-solid fa-gears",
        // TODO: better icon?
        onClick: /* @__PURE__ */ __name(() => {
          new ActorOnUseMacrosConfig({ document: app.document }).render({ force: true });
        }, "onClick")
      });
    }
  });
  Hooks.on("getHeaderControlsItemSheet5e", (app, controls) => {
    if ((game.user?.role ?? CONST.USER_ROLES.PLAYER) < (configSettings.midiPropertiesTabRole ?? CONST.USER_ROLES.PLAYER)) return;
    const title = "Midi-qol";
    controls.push({
      label: title,
      icon: "fa-solid fa-gears",
      // TODO: better icon?
      // @ts-expect-error v13-ism
      onClick: /* @__PURE__ */ __name(() => new ItemOnUseMacrosConfig({ document: app.document }).render({ force: true }), "onClick")
    });
  });
  Hooks.on("dropCanvasData", function(canvas2, dropData) {
    if (!dragDropTargeting) return true;
    if (dropData.type !== "Item") return true;
    if (!canvas2.grid?.grid) return;
    let grid_size = canvas2.scene?.grid.size ?? 100;
    let coords = { x: dropData.x, y: dropData.y };
    if (canvas2.scene?.grid.type !== CONST.GRID_TYPES.GRIDLESS) {
      coords = canvas2.grid.getCenterPoint(coords);
    }
    const targetCount = canvas2.tokens?.targetObjects({
      x: coords.x - 5,
      y: coords.y - 5,
      height: 10,
      width: 10
    }, { releaseOthers: true });
    if (targetCount === 0) {
      ui.notifications?.warn("No target selected");
      return true;
    }
    const item = fromUuidSync(dropData.uuid);
    if (!item) {
      const message = `actor / item broke for ${dropData?.uuid}`;
      error2(message);
      TroubleShooter.recordError(new Error(message), message);
    }
    item?.use({ legacy: false }, {}, {});
    return true;
  });
  setDnd5e53(foundry.utils.isNewerVersion(game.system.version, "5.2.99"));
  if (isDnd5e53) {
    Hooks.on("dnd5e.preCalculateDamage", preCalculateDamageHandler_53);
    Hooks.on("dnd5e.calculateDamage", calculateDamageHandler_53);
  } else {
    Hooks.on("dnd5e.preCalculateDamage", preCalculateDamageHandler);
    Hooks.on("dnd5e.calculateDamage", calculateDamageHandler);
  }
}
__name(initHooks, "initHooks");
function setupHooks() {
}
__name(setupHooks, "setupHooks");
Hooks.on("dnd5e.preRollDamage", (rollConfig, dialogConfig, messageConfig) => {
  if (!rollConfig.subject) return true;
  if (rollConfig.subject.actor && rollConfig.subject.isSpell) {
    const actorSpellBonus = foundry.utils.getProperty(rollConfig.subject, "system.bonuses.spell.all.damage");
    if (actorSpellBonus) rollConfig.rolls[0].parts.push(actorSpellBonus);
  }
  return true;
});
var SOURCE_TRAIT_CONFIG = {
  idi: "immunity",
  idr: "resistance",
  idv: "vulnerability",
  ida: "absorption",
  idm: "modification"
};
var CUSTOM_RESISTANCE_MATCHERS = {
  spell: {
    match: /* @__PURE__ */ __name((d) => d.properties?.has("spell") ?? false, "match"),
    label: "midi-qol.SpellDamage"
  },
  "non-spell": {
    match: /* @__PURE__ */ __name((d) => !d.properties?.has("spell"), "match"),
    label: "midi-qol.NonSpellDamage"
  },
  nonSpell: {
    match: /* @__PURE__ */ __name((d) => !d.properties?.has("spell"), "match"),
    label: "midi-qol.NonSpellDamage"
  },
  magical: {
    match: /* @__PURE__ */ __name((d) => d.properties?.has("mgc") ?? false, "match"),
    label: "midi-qol.Magical"
  },
  "non-magical": {
    match: /* @__PURE__ */ __name((d) => !d.properties?.has("mgc"), "match"),
    label: "midi-qol.NonMagical"
  },
  nonMagical: {
    match: /* @__PURE__ */ __name((d) => !d.properties?.has("mgc"), "match"),
    label: "midi-qol.NonMagical"
  },
  physical: {
    match: /* @__PURE__ */ __name((d, bypasses) => {
      const bypassesPresent = (d.properties ?? /* @__PURE__ */ new Set()).intersection(bypasses ?? /* @__PURE__ */ new Set());
      return (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && bypassesPresent.size === 0;
    }, "match"),
    label: "midi-qol.Physical"
  },
  "non-physical": {
    match: /* @__PURE__ */ __name((d) => !GameSystemConfig.damageTypes[d.type]?.isPhysical, "match"),
    label: "midi-qol.NonPhysical"
  },
  nonPhysical: {
    match: /* @__PURE__ */ __name((d) => !GameSystemConfig.damageTypes[d.type]?.isPhysical, "match"),
    label: "midi-qol.NonPhysical"
  },
  "non-magical-physical": {
    match: /* @__PURE__ */ __name((d) => (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && !d.properties?.has("mgc"), "match"),
    label: "midi-qol.NonMagicalPhysical"
  },
  nonMagicalPhysical: {
    match: /* @__PURE__ */ __name((d) => (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && !d.properties?.has("mgc"), "match"),
    label: "midi-qol.NonMagicalPhysical"
  },
  "non-silver-physical": {
    match: /* @__PURE__ */ __name((d) => (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && !d.properties?.has("sil"), "match"),
    label: "midi-qol.NonSilverPhysical"
  },
  nonSilverPhysical: {
    match: /* @__PURE__ */ __name((d) => (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && !d.properties?.has("sil"), "match"),
    label: "midi-qol.NonSilverPhysical"
  },
  "non-adamant-physical": {
    match: /* @__PURE__ */ __name((d) => (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && !d.properties?.has("ada"), "match"),
    label: "midi-qol.NonAdamantinePhysical"
  },
  nonAdamantPhysical: {
    match: /* @__PURE__ */ __name((d) => (GameSystemConfig.damageTypes[d.type]?.isPhysical ?? false) && !d.properties?.has("ada"), "match"),
    label: "midi-qol.NonAdamantinePhysical"
  },
  mwak: {
    match: /* @__PURE__ */ __name((d, bypasses) => {
      const bypassesPresent = (d.properties ?? /* @__PURE__ */ new Set()).intersection(bypasses ?? /* @__PURE__ */ new Set());
      return (d.properties?.has("mwak") ?? false) && bypassesPresent.size === 0;
    }, "match"),
    label: "mwak"
  },
  rwak: {
    match: /* @__PURE__ */ __name((d, bypasses) => {
      const bypassesPresent = (d.properties ?? /* @__PURE__ */ new Set()).intersection(bypasses ?? /* @__PURE__ */ new Set());
      return (d.properties?.has("rwak") ?? false) && bypassesPresent.size === 0;
    }, "match"),
    label: "rwak"
  },
  msak: {
    match: /* @__PURE__ */ __name((d) => d.properties?.has("msak") ?? false, "match"),
    label: "msak"
  },
  rsak: {
    match: /* @__PURE__ */ __name((d) => d.properties?.has("rsak") ?? false, "match"),
    label: "rsak"
  },
  all: {
    match: /* @__PURE__ */ __name(() => true, "match"),
    label: "All"
  }
};
function isDRType(type) {
  return ["none", "midi-none"].includes(type);
}
__name(isDRType, "isDRType");
function setDamageActiveFlag(damage, flag, scope = "type") {
  damage.active ??= {};
  if (isDnd5e53) {
    damage.active[scope] ??= {};
    damage.active[scope][flag] = true;
  } else {
    damage.active[flag] = true;
  }
}
__name(setDamageActiveFlag, "setDamageActiveFlag");
function getDamageActiveFlag(damage, flag) {
  if (isDnd5e53) {
    return !!(damage.active?.type?.[flag] || damage.active?.all?.[flag]);
  }
  return !!damage.active?.[flag];
}
__name(getDamageActiveFlag, "getDamageActiveFlag");
function shouldDowngrade(options, type) {
  return options.downgrade === true || options.downgrade?.has?.(type) === true;
}
__name(shouldDowngrade, "shouldDowngrade");
function shouldIgnore(options, category, type, actorTraits, skipDowngrade = false) {
  if (options.ignore === true) return true;
  if (options.ignore?.[category] === true) return true;
  if (options.ignore?.[category]?.has?.(type)) return true;
  if (options.ignore?.[category]?.has?.("all")) return true;
  if (category === "immunity" && shouldDowngrade(options, type) && !skipDowngrade) return true;
  if (category === "resistance" && shouldDowngrade(options, type)) {
    if (actorTraits?.di?.value?.has(type)) return false;
    return true;
  }
  return false;
}
__name(shouldIgnore, "shouldIgnore");
function hasTraitEffect(actorTraits, category, type, properties, options) {
  if (category === "dr" && shouldDowngrade(options ?? {}, type)) {
    if (hasTraitEffect(actorTraits, "di", type, properties) && !shouldIgnore(options ?? {}, "immunity", type, actorTraits, true)) {
      return true;
    }
  }
  const config = actorTraits[category];
  if (!config?.value?.has(type)) return false;
  if (!CONFIG.DND5E.damageTypes[type]?.isPhysical || !properties?.size) return true;
  return !config.bypasses?.intersection(properties)?.size;
}
__name(hasTraitEffect, "hasTraitEffect");
function computeSourceIgnores({ actor, sourceActor, damages, options }) {
  if (!sourceActor || options.midiIgnoreComputed) return;
  const actorTraits = actor.system?.traits ?? {};
  for (const [traitCode, category] of Object.entries(SOURCE_TRAIT_CONFIG)) {
    const trait = sourceActor.system?.traits?.[traitCode];
    if (!trait?.value?.size) continue;
    if (!options.ignore?.[category]) {
      foundry.utils.setProperty(options, `ignore.${category}`, /* @__PURE__ */ new Set());
    }
    for (const dt of Object.keys(GameSystemConfig.damageTypes)) {
      if (!trait.value.has(dt) && !trait.all) continue;
      if (!damages.some((di) => di.type === dt)) continue;
      if (category === "immunity" && hasTraitEffect(actorTraits, "di", dt) && !hasTraitEffect(actorTraits, "dr", dt)) {
        if (!(options.downgrade instanceof Set)) options.downgrade = /* @__PURE__ */ new Set();
        options.downgrade.add(dt);
      } else if (options.ignore) {
        options.ignore[category].add(dt);
      }
    }
  }
  options.midiIgnoreComputed = true;
}
__name(computeSourceIgnores, "computeSourceIgnores");
function applySaves({ damages, options }) {
  const mo = options.midi;
  if (!mo || options.ignore === true) return;
  for (const damage of damages) {
    if (isDRType(damage.type)) continue;
    const ignoringSuperSaver = mo.superSaver && shouldIgnore(options, "superSaver", damage.type);
    const ignoringSemiSuperSaver = mo.semiSuperSaver && shouldIgnore(options, "semiSuperSaver", damage.type);
    const ignoringSaved = mo.saved && shouldIgnore(options, "saved", damage.type);
    if (mo.superSaver && !ignoringSuperSaver) {
      setDamageActiveFlag(damage, "superSaver");
    }
    if (mo.semiSuperSaver && !ignoringSemiSuperSaver && (mo.saveMultiplier ?? 1) !== 1) {
      setDamageActiveFlag(damage, "semiSuperSaver");
    }
    if (mo.saved && !ignoringSaved && (mo.saveMultiplier ?? 1) !== 1) {
      setDamageActiveFlag(damage, "saved");
    }
    if (mo.saved && !ignoringSaved) {
      const multiplier = ignoringSuperSaver ? configSettings.defaultSaveMultiplier : mo.superSaver ? 0 : mo.saveMultiplier ?? 1;
      damage.value = damage.value * multiplier;
      foundry.utils.setProperty(damage, "active.multiplier", (damage.active?.multiplier ?? 1) * multiplier);
    } else if (mo.superSaver && !ignoringSuperSaver) {
      damage.value = damage.value * configSettings.defaultSaveMultiplier;
      foundry.utils.setProperty(damage, "active.multiplier", (damage.active?.multiplier ?? 1) * configSettings.defaultSaveMultiplier);
    }
  }
}
__name(applySaves, "applySaves");
function applyUncannyDodge({ actor, damages, options }) {
  const mo = options.midi;
  if (!mo?.uncannyDodge || options.ignore === true) return;
  const actorTraits = actor.system?.traits ?? {};
  for (const damage of damages) {
    if (shouldIgnore(options, "uncannyDodge", damage.type, actorTraits, true)) continue;
    setDamageActiveFlag(damage, "uncannyDodge");
    foundry.utils.setProperty(damage, "multiplier", (damage.multiplier ?? 1) * 0.5);
    damage.value = damage.value * 0.5;
  }
}
__name(applyUncannyDodge, "applyUncannyDodge");
function applyTraitMultipliers({ actor, damages, options }) {
  if (options.midi?.noCalc) return;
  const actorTraits = actor.system?.traits ?? {};
  for (const damage of damages) {
    if (damage.active?.resistance) {
      damage.value = damage.value * 2 * configSettings.damageResistanceMultiplier;
      damage.active.multiplier = (damage.active.multiplier ?? 1) * 2 * configSettings.damageResistanceMultiplier;
    }
    if (damage.active?.vulnerability) {
      damage.active.multiplier = (damage.active.multiplier ?? 1) / 2 * configSettings.damageVulnerabilityMultiplier;
      damage.value = damage.value / 2 * configSettings.damageVulnerabilityMultiplier;
    }
    const absorptionValue = actorTraits.da?.[damage.type];
    if (absorptionValue !== void 0 && !shouldIgnore(options, "absorption", damage.type, actorTraits)) {
      const multiplier = Number(absorptionValue) ?? -1;
      damage.active ??= {};
      damage.active.multiplier = (damage.active.multiplier ?? 1) * multiplier;
      damage.value *= multiplier;
      damage.damage = damage.value;
      damage.active.absorption = true;
    }
  }
}
__name(applyTraitMultipliers, "applyTraitMultipliers");
function applyCustomResistances({ actor, damages, options }) {
  const categories = { di: "immunity", dr: "resistance", dv: "vulnerability", da: "absorption" };
  const traitMultipliers = {
    dr: configSettings.damageResistanceMultiplier,
    di: configSettings.damageImmunityMultiplier,
    da: -1,
    dv: configSettings.damageVulnerabilityMultiplier
  };
  const actorTraits = actor.system?.traits ?? {};
  for (const trait of ["da", "dv", "di", "dr"]) {
    const traitData = actorTraits[trait];
    if (!traitData) continue;
    const bypasses = traitData.bypasses ?? /* @__PURE__ */ new Set();
    const customs = [
      ...(traitData.custom ?? "").split(";").map((s) => s.trim()),
      ...Object.keys(traitData.midi ?? {})
    ].filter((c) => c !== "");
    for (let custom of customs) {
      const normalizedCustom = Object.keys(GameSystemConfig.customDamageResistanceTypes ?? {}).find((key) => GameSystemConfig.customDamageResistanceTypes[key].toLowerCase() === custom.toLowerCase()) ?? custom;
      const matcher = CUSTOM_RESISTANCE_MATCHERS[custom] ?? CUSTOM_RESISTANCE_MATCHERS[normalizedCustom];
      for (const damage of damages) {
        if (damage.type === "midi-none") continue;
        if (GameSystemConfig.healingTypes[damage.type]) continue;
        if (damage.active?.[categories[trait]]) continue;
        if (damage.active?.[custom]) continue;
        if (shouldIgnore(options, categories[trait], damage.type, actorTraits)) continue;
        if (shouldIgnore(options, custom, damage.type, actorTraits)) continue;
        const matches = matcher ? matcher.match(damage, bypasses) : damage.properties?.has(custom);
        if (!matches) continue;
        damage.active ??= {};
        damage.active[GameSystemConfig.customDamageResistanceTypes?.[normalizedCustom] ?? custom] = true;
        damage.active[categories[trait]] = true;
        let multiplier = traitMultipliers[trait];
        const daOverride = actorTraits.da?.midi?.[custom] ?? actorTraits.da?.midi?.all;
        if (daOverride && Number.isNumeric(daOverride)) {
          multiplier = Number(daOverride);
        }
        damage.active.multiplier = (damage.active.multiplier ?? 1) * multiplier;
        damage.value = damage.value * multiplier;
        damage.damage = damage.value;
      }
    }
  }
}
__name(applyCustomResistances, "applyCustomResistances");
function applyDamageReduction({ actor, damages, options }) {
  if (options.ignore === true) return true;
  if (options.ignore?.DR?.has("none") || options.ignore?.DR?.has("all")) return true;
  const actorTraits = actor.system?.traits ?? {};
  const specials = Object.keys(actorTraits?.dm?.midi ?? {});
  if (!specials.length) return true;
  if (configSettings.saveDROrder === "DRSaveDr") {
    return applyDamageReductionInline({ actor, damages, options }, specials, actorTraits);
  }
  let drAllActives = [];
  let dmAll = 0;
  const selectDamages = /* @__PURE__ */ __name((predicate) => {
    const bypasses = actorTraits.dm?.bypasses ?? /* @__PURE__ */ new Set();
    return damages.reduce((total, damage) => {
      if (damage.value === 0 || isDRType(damage.type)) return total;
      return total + (predicate(damage, bypasses) ? damage.value : 0);
    }, 0);
  }, "selectDamages");
  for (const special of specials) {
    if (options.ignore?.modification === true) continue;
    if (options.ignore?.modification?.has?.(special)) continue;
    const bypasses = actorTraits.dm?.bypasses ?? /* @__PURE__ */ new Set();
    const dmRoll = new Roll(`${actorTraits.dm.midi[special]}`, actor.getRollData());
    const dm = doSyncRoll(dmRoll, `traits.dm.midi.${special}`)?.total ?? 0;
    if (!dm) continue;
    const matcher = CUSTOM_RESISTANCE_MATCHERS[special];
    let selectedDamage;
    let dmActive = "";
    if (matcher) {
      selectedDamage = selectDamages((d) => !GameSystemConfig.healingTypes[d.type] && matcher.match(d, bypasses));
      if (selectedDamage > 0) dmActive = i18n(matcher.label);
    } else {
      selectedDamage = 0;
    }
    if (selectedDamage > 0 && dm) {
      let appliedDm = dm;
      if (Math.sign(selectedDamage + appliedDm) !== Math.sign(selectedDamage)) {
        appliedDm = -selectedDamage;
      }
      if (checkRule("maxDRValue") && (appliedDm < dmAll || dmAll === 0)) {
        dmAll = appliedDm;
        drAllActives = [dmActive];
      } else if (!checkRule("maxDRValue")) {
        drAllActives.push(dmActive);
        dmAll += appliedDm;
      }
    }
  }
  if (!dmAll) return true;
  const { totalDamage } = damages.reduce((acc, d) => {
    if (d.type === "temphp") acc.temp += d.value;
    else if (d.type !== "midi-none" && d.type !== "vitality") acc.totalDamage += d.value;
    return acc;
  }, { totalDamage: 0, temp: 0 });
  if (Math.sign(totalDamage) !== Math.sign(dmAll + totalDamage)) {
    dmAll = -totalDamage;
  }
  if (dmAll) {
    const drEntry = {
      type: "none",
      value: dmAll,
      active: { multiplier: 1 },
      allActives: drAllActives,
      properties: /* @__PURE__ */ new Set()
    };
    setDamageActiveFlag(drEntry, "DR");
    damages.push(drEntry);
  }
  for (let i = damages.length - 1; i >= 0; i--) {
    if (damages[i].type === "midi-none") {
      damages.splice(i, 1);
    }
  }
  return true;
}
__name(applyDamageReduction, "applyDamageReduction");
function applyDamageReductionInline({ actor, damages, options }, specials, actorTraits) {
  const useMaxDR = checkRule("maxDRValue");
  let totalDRApplied = 0;
  const drAllActives = [];
  function distributeProportionally(drLabel, drAmount, predicate) {
    const bypasses = actorTraits.dm?.bypasses ?? /* @__PURE__ */ new Set();
    const matching = damages.filter(
      (d) => d.value > 0 && !isDRType(d.type) && !GameSystemConfig.healingTypes[d.type] && predicate(d, bypasses)
    );
    const totalMatching = matching.reduce((sum, d) => sum + d.value, 0);
    if (totalMatching <= 0 || drAmount >= 0) return 0;
    let effectiveDR = drAmount;
    if (Math.abs(effectiveDR) > totalMatching) effectiveDR = -totalMatching;
    for (const entry of matching) {
      const share = entry.value / totalMatching * Math.abs(effectiveDR);
      entry.value -= share;
    }
    return effectiveDR;
  }
  __name(distributeProportionally, "distributeProportionally");
  function computeDR(special) {
    if (options.ignore === true) return 0;
    if (options.ignore?.modification === true) return 0;
    if (options.ignore?.modification?.has?.(special)) return 0;
    const dmRoll = new Roll(`${actorTraits.dm.midi[special]}`, actor.getRollData());
    return doSyncRoll(dmRoll, `traits.dm.midi.${special}`)?.total ?? 0;
  }
  __name(computeDR, "computeDR");
  const specificSpecials = specials.filter((s) => s !== "all");
  if (useMaxDR) {
    let bestDR = 0;
    let bestSpecial = "";
    let bestMatcher;
    for (const special of specificSpecials) {
      const dm = computeDR(special);
      if (!dm) continue;
      const matcher = CUSTOM_RESISTANCE_MATCHERS[special];
      if (!matcher) continue;
      const bypasses = actorTraits.dm?.bypasses ?? /* @__PURE__ */ new Set();
      const selectedDamage = damages.filter((d) => d.value > 0 && !isDRType(d.type) && !GameSystemConfig.healingTypes[d.type] && matcher.match(d, bypasses)).reduce((sum, d) => sum + d.value, 0);
      const effectiveImpact = Math.min(Math.abs(dm), selectedDamage);
      if (effectiveImpact > Math.abs(bestDR)) {
        bestDR = dm;
        bestSpecial = special;
        bestMatcher = matcher;
      }
    }
    if (bestDR && bestMatcher) {
      const applied = distributeProportionally(bestSpecial, bestDR, (d, bypasses) => bestMatcher.match(d, bypasses));
      totalDRApplied += Math.abs(applied);
      drAllActives.push(i18n(bestMatcher.label));
    }
  } else {
    for (const special of specificSpecials) {
      const dm = computeDR(special);
      if (!dm) continue;
      const matcher = CUSTOM_RESISTANCE_MATCHERS[special];
      if (!matcher) continue;
      const applied = distributeProportionally(special, dm, (d, bypasses) => matcher.match(d, bypasses));
      if (applied) {
        totalDRApplied += Math.abs(applied);
        drAllActives.push(i18n(matcher.label));
      }
    }
  }
  if (specials.includes("all")) {
    const dm = computeDR("all");
    if (dm) {
      if (useMaxDR) {
        const remaining = damages.filter((d) => d.value > 0 && !isDRType(d.type) && !GameSystemConfig.healingTypes[d.type]).reduce((sum, d) => sum + d.value, 0);
        const allImpact = Math.min(Math.abs(dm), remaining);
        if (allImpact > totalDRApplied) {
          const topUp = -(allImpact - totalDRApplied);
          const applied = distributeProportionally("all (top-up)", topUp, () => true);
          totalDRApplied += Math.abs(applied);
          drAllActives.push(i18n(CUSTOM_RESISTANCE_MATCHERS["all"].label));
        }
      } else {
        const applied = distributeProportionally("all", dm, () => true);
        if (applied) {
          totalDRApplied += Math.abs(applied);
          drAllActives.push(i18n(CUSTOM_RESISTANCE_MATCHERS["all"].label));
        }
      }
    }
  }
  if (totalDRApplied) {
    const drEntry = {
      type: "none",
      value: 0,
      active: { multiplier: 1, inlined: true },
      allActives: drAllActives,
      properties: /* @__PURE__ */ new Set(),
      displayValue: -totalDRApplied
    };
    setDamageActiveFlag(drEntry, "DR");
    damages.push(drEntry);
  }
  for (let i = damages.length - 1; i >= 0; i--) {
    if (damages[i].type === "midi-none") damages.splice(i, 1);
  }
  return true;
}
__name(applyDamageReductionInline, "applyDamageReductionInline");
function recalculateTotals({ damages }) {
  damages.amount = 0;
  damages.temp = 0;
  for (const damage of damages) {
    if (damage.active?.inlined) continue;
    if (damage.type === "temphp") {
      damages.temp += damage.value;
    } else {
      damages.amount += damage.value;
    }
  }
  damages.amount = damages.amount > 0 ? Math.floor(damages.amount) : Math.ceil(damages.amount);
  if (damages.amount < 0) {
    for (const damage of damages) {
      if (damage.type === "none" && damage.active?.DR && !damage.active?.inlined) {
        damage.value -= damages.amount;
        damages.amount = 0;
        break;
      }
    }
  }
}
__name(recalculateTotals, "recalculateTotals");
function calculateTotalDamage({ damages, options }) {
  return damages.reduce((total, damage) => {
    if (damage.active?.inlined) return total;
    let value = damage.value;
    if (options.invertHealing !== false && damage.type === "healing") value = -value;
    if (damage.type === "temphp" || damage.type === "midi-none") value = 0;
    return total + value;
  }, 0);
}
__name(calculateTotalDamage, "calculateTotalDamage");
function applyTraitMultipliers_53({ actor, damages, options }) {
  if (options.midi?.noCalc) return;
  const actorTraits = actor.system?.traits ?? {};
  for (const damage of damages) {
    const hasResistance = damage.active?.type?.resistance || damage.active?.all?.resistance;
    const hasVulnerability = damage.active?.type?.vulnerability || damage.active?.all?.vulnerability;
    if (hasResistance) {
      damage.value = damage.value * 2 * configSettings.damageResistanceMultiplier;
      damage.active ??= {};
      damage.active.multiplier = (damage.active.multiplier ?? 1) * 2 * configSettings.damageResistanceMultiplier;
    }
    if (hasVulnerability) {
      damage.active ??= {};
      damage.active.multiplier = (damage.active.multiplier ?? 1) / 2 * configSettings.damageVulnerabilityMultiplier;
      damage.value = damage.value / 2 * configSettings.damageVulnerabilityMultiplier;
    }
    const absorptionValue = actorTraits.da?.[damage.type];
    if (absorptionValue !== void 0 && !shouldIgnore(options, "absorption", damage.type, actorTraits)) {
      const multiplier = Number(absorptionValue) ?? -1;
      damage.active ??= {};
      damage.active.multiplier = (damage.active.multiplier ?? 1) * multiplier;
      damage.value *= multiplier;
      damage.damage = damage.value;
      setDamageActiveFlag(damage, "absorption");
    }
  }
}
__name(applyTraitMultipliers_53, "applyTraitMultipliers_53");
function applyCustomResistances_53({ actor, damages, options }) {
  const categories = { di: "immunity", dr: "resistance", dv: "vulnerability", da: "absorption" };
  const traitMultipliers = {
    dr: configSettings.damageResistanceMultiplier,
    di: configSettings.damageImmunityMultiplier,
    da: -1,
    dv: configSettings.damageVulnerabilityMultiplier
  };
  const actorTraits = actor.system?.traits ?? {};
  for (const trait of ["da", "dv", "di", "dr"]) {
    const traitData = actorTraits[trait];
    if (!traitData) continue;
    const bypasses = traitData.bypasses ?? /* @__PURE__ */ new Set();
    const customs = [
      ...(traitData.custom ?? "").split(";").map((s) => s.trim()),
      ...Object.keys(traitData.midi ?? {})
    ].filter((c) => c !== "");
    for (let custom of customs) {
      const normalizedCustom = Object.keys(GameSystemConfig.customDamageResistanceTypes ?? {}).find((key) => GameSystemConfig.customDamageResistanceTypes[key].toLowerCase() === custom.toLowerCase()) ?? custom;
      const matcher = CUSTOM_RESISTANCE_MATCHERS[custom] ?? CUSTOM_RESISTANCE_MATCHERS[normalizedCustom];
      for (const damage of damages) {
        if (damage.type === "midi-none") continue;
        if (GameSystemConfig.healingTypes[damage.type]) continue;
        if (getDamageActiveFlag(damage, categories[trait])) continue;
        if (getDamageActiveFlag(damage, custom)) continue;
        if (shouldIgnore(options, categories[trait], damage.type, actorTraits)) continue;
        if (shouldIgnore(options, custom, damage.type, actorTraits)) continue;
        const matches = matcher ? matcher.match(damage, bypasses) : damage.properties?.has(custom);
        if (!matches) continue;
        setDamageActiveFlag(damage, GameSystemConfig.customDamageResistanceTypes?.[normalizedCustom] ?? custom);
        setDamageActiveFlag(damage, categories[trait]);
        damage.active ??= {};
        let multiplier = traitMultipliers[trait];
        const daOverride = actorTraits.da?.midi?.[custom] ?? actorTraits.da?.midi?.all;
        if (daOverride && Number.isNumeric(daOverride)) {
          multiplier = Number(daOverride);
        }
        damage.active.multiplier = (damage.active.multiplier ?? 1) * multiplier;
        damage.value = damage.value * multiplier;
        damage.damage = damage.value;
      }
    }
  }
}
__name(applyCustomResistances_53, "applyCustomResistances_53");
function recalculateTotals_53({ damages }) {
  damages.amount = 0;
  damages.temp = 0;
  damages.tempMax = 0;
  for (const damage of damages) {
    if (damage.active?.inlined) continue;
    if (damage.type === "temphp") {
      damages.temp += damage.value;
    } else if (damage.type === "maximum") {
      damages.tempMax += damage.value;
    } else {
      damages.amount += damage.value;
    }
  }
  damages.amount = damages.amount > 0 ? Math.floor(damages.amount) : Math.ceil(damages.amount);
  if (damages.amount < 0) {
    for (const damage of damages) {
      if (damage.type === "none" && getDamageActiveFlag(damage, "DR") && !damage.active?.inlined) {
        damage.value -= damages.amount;
        damages.amount = 0;
        break;
      }
    }
  }
}
__name(recalculateTotals_53, "recalculateTotals_53");
function calculateTotalDamage_53({ damages, options }) {
  return damages.reduce((total, damage) => {
    if (damage.active?.inlined) return total;
    let value = damage.value;
    if (options.invertHealing !== false && damage.type === "healing") value = -value;
    if (damage.type === "temphp" || damage.type === "midi-none" || damage.type === "maximum") value = 0;
    return total + value;
  }, 0);
}
__name(calculateTotalDamage_53, "calculateTotalDamage_53");
function preCalculateDamageHandler(actor, damages, options) {
  try {
    if (options.midi?.noCalc) return true;
    const sourceActor = options.midi?.sourceActorUuid ? fromUuidSync(options.midi.sourceActorUuid) : void 0;
    const params = { actor, sourceActor, damages, options };
    const saveDROrder = configSettings.saveDROrder;
    computeSourceIgnores(params);
    applyUncannyDodge(params);
    if (saveDROrder === "DRSaveDr") {
      if (!applyDamageReduction(params)) return false;
    } else if (["SaveDrDR", "SaveDRDr"].includes(saveDROrder)) {
      applySaves(params);
      if (saveDROrder === "SaveDRDr") {
        if (!applyDamageReduction(params)) return false;
      }
    }
    foundry.utils.setProperty(options, "midi.totalDamage", calculateTotalDamage(params));
    if (Hooks.call("midi-qol.dnd5ePreCalculateDamage", actor, damages, options) === false) return false;
  } catch (err) {
    const message = `Error in preCalculateDamage`;
    error2(message, err);
    TroubleShooter.recordError(err, message);
  }
  return true;
}
__name(preCalculateDamageHandler, "preCalculateDamageHandler");
function calculateDamageHandler(actor, damages, options) {
  try {
    if (options.midi?.noCalc) return true;
    const sourceActor = options.midi?.sourceActorUuid ? fromUuidSync(options.midi.sourceActorUuid) : void 0;
    const params = { actor, sourceActor, damages, options };
    const saveDROrder = configSettings.saveDROrder;
    if (saveDROrder === "DRSaveDr") {
      applySaves(params);
    }
    applyTraitMultipliers(params);
    applyCustomResistances(params);
    if (saveDROrder === "SaveDrDR") {
      if (!applyDamageReduction(params)) return false;
    }
    recalculateTotals(params);
    if (Hooks.call("midi-qol.dnd5eCalculateDamage", actor, damages, options) === false) return false;
  } catch (err) {
    error2(err);
  }
  return true;
}
__name(calculateDamageHandler, "calculateDamageHandler");
function preCalculateDamageHandler_53(actor, damages, options) {
  try {
    if (options.midi?.noCalc) return true;
    const sourceActor = options.midi?.sourceActorUuid ? fromUuidSync(options.midi.sourceActorUuid) : void 0;
    const params = { actor, sourceActor, damages, options };
    const saveDROrder = configSettings.saveDROrder;
    computeSourceIgnores(params);
    applyUncannyDodge(params);
    if (saveDROrder === "DRSaveDr") {
      if (!applyDamageReduction(params)) return false;
    } else if (["SaveDrDR", "SaveDRDr"].includes(saveDROrder)) {
      applySaves(params);
      if (saveDROrder === "SaveDRDr") {
        if (!applyDamageReduction(params)) return false;
      }
    }
    foundry.utils.setProperty(options, "midi.totalDamage", calculateTotalDamage_53(params));
    if (Hooks.call("midi-qol.dnd5ePreCalculateDamage", actor, damages, options) === false) return false;
  } catch (err) {
    const message = `Error in preCalculateDamage`;
    error2(message, err);
    TroubleShooter.recordError(err, message);
  }
  return true;
}
__name(preCalculateDamageHandler_53, "preCalculateDamageHandler_53");
function calculateDamageHandler_53(actor, damages, options) {
  try {
    if (options.midi?.noCalc) return true;
    const sourceActor = options.midi?.sourceActorUuid ? fromUuidSync(options.midi.sourceActorUuid) : void 0;
    const params = { actor, sourceActor, damages, options };
    const saveDROrder = configSettings.saveDROrder;
    if (saveDROrder === "DRSaveDr") {
      applySaves(params);
    }
    applyTraitMultipliers_53(params);
    applyCustomResistances_53(params);
    if (saveDROrder === "SaveDrDR") {
      if (!applyDamageReduction(params)) return false;
    }
    recalculateTotals_53(params);
    if (Hooks.call("midi-qol.dnd5eCalculateDamage", actor, damages, options) === false) return false;
  } catch (err) {
    error2(err);
  }
  return true;
}
__name(calculateDamageHandler_53, "calculateDamageHandler_53");
Hooks.on("dnd5e.preApplyDamage", (actor, amount, updates, options) => {
  if (updates["system.attributes.hp.temp"]) updates["system.attributes.hp.temp"] = Math.floor(updates["system.attributes.hp.temp"]);
  const vitalityItem = getVitalityItem(actor);
  if (amount > 0 && vitalityItem && !options.midiVitalityHandled) {
    const currentHP = actor.system.attributes.hp.value ?? 0;
    const currentTempHP = actor.system.attributes.hp.temp ?? 0;
    const totalHP = currentTempHP + currentHP;
    if (amount > totalHP) {
      const overflowDamage = amount - totalHP;
      const currentSpent = vitalityItem.system.uses?.spent ?? 0;
      const maxUses = vitalityItem.system.uses?.max ?? 0;
      const newSpent = Math.min(maxUses, currentSpent + overflowDamage);
      options.midiVitalityHandled = true;
      options.midiVitalityUpdatePending = true;
      setTimeout(async () => {
        await vitalityItem.update({ "system.uses.spent": newSpent });
      }, 100);
    }
  }
  if (options.midi) {
    foundry.utils.setProperty(options, "midi.amount", amount);
    foundry.utils.setProperty(options, "midi.updates", updates);
  }
  return true;
});
Hooks.on("dnd5e.preRollConcentration", (rollConfig, dialogConfig, messageConfig) => {
  if (rollConfig.workflowOptions?.noConcentrationCheck) return false;
  return true;
});
Hooks.on("dnd5e.rollConcentration", (rolls, { subject }) => {
  if (!subject || !(subject instanceof CONFIG.Actor.documentClass)) {
    const message = "dnd5e.rollConcentration hook called with non-actor";
    TroubleShooter.recordError(new Error(message), message);
    error2(message, subject);
    return;
  }
  if (rolls instanceof Roll) rolls = [rolls];
  let failed = false;
  for (let roll of rolls) {
    if (roll.isFailure) failed = true;
  }
  if (configSettings.keepRollStats) {
    gameStats.addConcentrationResult(subject, !failed);
  }
  if (configSettings.doConcentrationCheck === "item") return;
  for (let roll of rolls) {
    if (checkRule("criticalSaves") && roll.isCritical) roll.options.success = true;
  }
  if (configSettings.removeConcentration && failed) {
    subject.endConcentration();
  }
});
Hooks.on("dnd5e.preCreateActivityTemplate", (activity, templateData) => {
  templateData.distance += 1e-6;
  if (!configSettings.constrainTemplatePlacement) return true;
  const actor = activity.actor;
  const token = actor ? tokenForActor(actor) : void 0;
  if (token) {
    foundry.utils.setProperty(templateData, "flags.midi-qol.sourceTokenUuid", token.document.uuid);
  }
  const range = activity.range;
  const rangeUnits = range?.units;
  if (rangeUnits && rangeUnits !== "self" && rangeUnits !== "none" && rangeUnits !== "any") {
    let maxRange = range.long || range.value;
    if (maxRange && maxRange > 0) {
      if (rangeUnits === "touch") maxRange = canvas?.dimensions?.distance ?? 5;
      else maxRange = rangeToSceneUnits(maxRange, rangeUnits);
      foundry.utils.setProperty(templateData, "flags.midi-qol.maxRange", maxRange);
    }
  }
  const templateType = templateData.t;
  if (templateType === "cone" || templateType === "ray") {
    const isSelfRange = rangeUnits === "self" || rangeUnits === "none" || !range?.value && !range?.long && rangeUnits !== "touch";
    if (isSelfRange) {
      foundry.utils.setProperty(templateData, "flags.midi-qol.anchorToToken", true);
    }
  }
  return true;
});
Hooks.on("preCreateMeasuredTemplate", (templateDoc, templateData, options, userId) => {
  if (userId !== game.user?.id) return true;
  const midiFlags2 = templateDoc.flags?.[MODULE_ID];
  const maxRange = midiFlags2?.maxRange;
  if (!maxRange) return true;
  const sourceTokenUuid = midiFlags2?.sourceTokenUuid;
  if (!sourceTokenUuid) return true;
  const tokenDoc = fromUuidSync(sourceTokenUuid);
  const token = tokenDoc?.object;
  if (!token) return true;
  const templateX = templateDoc.x;
  const templateY = templateDoc.y;
  if (templateX === void 0 || templateY === void 0) return true;
  const poly = getTokenPerimeterPolygon(token);
  const closest = closestPointOnPerimeter(poly, templateX, templateY);
  const distance = measurePointDistance(closest, { x: templateX, y: templateY });
  const tolerance = (canvas?.dimensions?.distance ?? 5) / 2;
  if (distance <= maxRange + tolerance) return true;
  ui.notifications?.warn(i18n("midi-qol.TemplateOutOfRange"));
  return false;
});
Hooks.on("dnd5e.preConfigureInitiative", (actor, rollConfig) => {
  let { parts, data, options } = rollConfig;
  const init = actor.system.attributes.init.value ?? "dex";
  const conditionData = createConditionData({ workflow: void 0, target: void 0, actor });
  if (evalAllConditions(actor, "flags.midi-qol.advantage.all", conditionData) || evalAllConditions(actor, "flags.midi-qol.advantage.check.all", conditionData) || evalAllConditions(actor, `flags.midi-qol.advantage.check.${init}`, conditionData) || evalAllConditions(actor, `flags.${game.system?.id}.initiativeAdv`, conditionData)) {
    options.advantage ||= true;
  }
  if (evalAllConditions(actor, "flags.midi-qol.disadvantage.all", conditionData) || evalAllConditions(actor, "flags.midi-qol.disadvantage.check.all", conditionData) || evalAllConditions(actor, `flags.midi-qol.disadvantage.check.${init}`, conditionData) || evalAllConditions(actor, `flags.${game.system?.id}.initiativeDisadv`, conditionData)) {
    options.disadvantage ||= true;
  }
  if (evalAllConditions(actor, "flags.midi-qol.noAdvantage.all", conditionData) || evalAllConditions(actor, "flags.midi-qol.noAdvantage.initiative", conditionData) || evalAllConditions(actor, `flags.${game.system?.id}.initiativeNoAdv`, conditionData)) {
    options.advantage = false;
  }
  if (evalAllConditions(actor, "flags.midi-qol.noDisadvantage.all", conditionData) || evalAllConditions(actor, "flags.midi-qol.noDisadvantage.initiative", conditionData) || evalAllConditions(actor, `flags.${game.system?.id}.initiativeNoDisadv`, conditionData)) {
    options.disadvantage = false;
  }
  if (foundry.utils.getProperty(actor, `flags.${game.system?.id}.initiativeHalfProficiency`) && !parts.includes("@prof")) {
    parts.push("@prof");
    data.prof = new globalThis.dnd5e.documents.Proficiency(data.attributes.prof, 0.5, false);
  }
});

// src/module/utils.ts
var Token2 = foundry.canvas.placeables.Token;
var { ApplicationV2: ApplicationV211, DialogV2: DialogV25, HandlebarsApplicationMixin: HandlebarsApplicationMixin12 } = foundry.applications.api;
var defaultTimeout = 30;
function midiFlag2(flag) {
  return `flags.${MODULE_ID}.${flag}`;
}
__name(midiFlag2, "midiFlag");
function getDamageType(flavorString = "") {
  if (flavorString === "") return "none";
  if (GameSystemConfig.damageTypes[flavorString] !== void 0) {
    return flavorString;
  }
  if (GameSystemConfig.healingTypes[flavorString] !== void 0) {
    return flavorString;
  }
  const validDamageTypes = Object.entries(GameSystemConfig.damageTypes).map(([key, { label }]) => [label.toLowerCase(), key]);
  const validHealingTypes = Object.entries(GameSystemConfig.healingTypes).map(([key, { label }]) => [label.toLowerCase(), key]);
  const allTypes = Object.fromEntries(validDamageTypes.concat(validHealingTypes));
  return allTypes[flavorString.toLowerCase()];
}
__name(getDamageType, "getDamageType");
function modifyDamageBy({
  damageItem,
  value,
  multiplier = 1,
  type = "none",
  reason
}) {
  if (!damageItem || foundry.utils.isEmpty(damageItem)) return {};
  if (!value) return {};
  const damageModification = { value, active: { multiplier }, type, properties: /* @__PURE__ */ new Set() };
  if (!configSettings.useDamageDetail) damageItem.hpDamage += value;
  damageItem.damageDetail.push(damageModification);
  if (reason) damageItem.details.push(reason);
  return damageModification;
}
__name(modifyDamageBy, "modifyDamageBy");
function createDamageDetail({
  roll,
  activity,
  defaultType = MidiQOL.MQdefaultDamageType
}) {
  let rolls = roll;
  if (rolls instanceof Roll) {
    rolls = [rolls];
  }
  if (activity?.damage?.parts[0]) {
    defaultType = activity.damage.parts[0].types.first() ?? defaultType;
  }
  rolls = rolls.map((r) => {
    r = foundry.utils.deepClone(r);
    if (!r.options.type) r.options.type = defaultType;
    return r;
  });
  const aggregatedRolls = game.system.dice.aggregateDamageRolls(rolls, { respectProperties: true });
  let detail = aggregatedRolls.map((roll2) => {
    const value = roll2.options.type !== "healing" ? Math.max(0, roll2.total) : roll2.total;
    return { value, damage: value, type: roll2.options.type, formula: roll2.formula, properties: new Set(roll2.options.properties ?? []) };
  });
  return detail;
}
__name(createDamageDetail, "createDamageDetail");
function getOrCreateTokenForActor(actor) {
  if (!actor) return;
  if (actor.token?.object) return actor.token.object;
  const token = tokenForActor(actor);
  if (token) return token;
  const tokenData = actor.prototypeToken.toObject();
  tokenData.actorId = actor.id;
  const cls = globalThis.getDocumentClass("Token");
  const tokenDocument = new cls(tokenData, { parent: canvas.scene });
  return new CONFIG.Token.objectClass(tokenDocument);
}
__name(getOrCreateTokenForActor, "getOrCreateTokenForActor");
function getOrCreateTokenForActorAsSet(actor) {
  const selfTarget = getOrCreateTokenForActor(actor);
  if (selfTarget) return /* @__PURE__ */ new Set([selfTarget]);
  return /* @__PURE__ */ new Set();
}
__name(getOrCreateTokenForActorAsSet, "getOrCreateTokenForActorAsSet");
var getTraitMult = /* @__PURE__ */ __name((actor, dmgTypeString, item, damageProperties = []) => {
  dmgTypeString = getDamageType(dmgTypeString) ?? "";
  let totalMult = 1;
  if (["healing", "temphp"].includes(dmgTypeString)) totalMult = -1;
  if (dmgTypeString === "midi-none") return 0;
  if (configSettings.damageImmunities === "none") return totalMult;
  const physicalDamageTypes = Object.keys(GameSystemConfig.damageTypes).filter((dt) => GameSystemConfig.damageTypes[dt].isPhysical);
  if (dmgTypeString !== "") {
    let magicalDamage = item.system.magicAvailable;
    magicalDamage ??= item?.system.properties?.has("mgc");
    magicalDamage ||= configSettings.requireMagical === "off" && (item?.system.magicalBonus > 0 || item?.system.attackBonus > 0);
    magicalDamage ||= configSettings.requireMagical === "off" && item?.type !== "weapon";
    magicalDamage ||= configSettings.requireMagical === "nonspell" && item?.type === "spell";
    magicalDamage ||= damageProperties.includes("mgc");
    const silverDamage = item?.system.properties.has("sil") || magicalDamage || damageProperties.includes("sil");
    const adamantineDamage = item?.system.properties?.has("ada") || damageProperties.includes("ada");
    const physicalDamage = physicalDamageTypes.includes(dmgTypeString);
    let traitList2 = [
      { type: "di", mult: configSettings.damageImmunityMultiplier },
      { type: "dr", mult: configSettings.damageResistanceMultiplier },
      { type: "dv", mult: configSettings.damageVulnerabilityMultiplier }
    ];
    for (let { type, mult } of traitList2) {
      if (!actor.system.traits) continue;
      let trait = foundry.utils.deepClone(actor.system.traits[type].value);
      let customs = [];
      if (actor.system.traits[type].custom?.length > 0) {
        customs = actor.system.traits[type].custom.split(";").map((s) => s.trim());
      }
      const bypasses = actor.system.traits[type].bypasses ?? /* @__PURE__ */ new Set();
      const itemProperties = item?.system.properties ?? /* @__PURE__ */ new Set();
      let bypassTrait = itemProperties.intersection(bypasses).size > 0;
      if (physicalDamage && bypassTrait) continue;
      if (!["healing", "temphp"].includes(dmgTypeString)) {
        if (customs.includes(dmgTypeString) || trait.has(dmgTypeString)) {
          totalMult = totalMult * mult;
          continue;
        }
        if (!magicalDamage && (trait.has("nonmagic") || customs.includes(GameSystemConfig.customDamageResistanceTypes?.nonmagic))) {
          totalMult = totalMult * mult;
          continue;
        } else if (!magicalDamage && physicalDamage && (trait.has("physical") || customs.includes(GameSystemConfig.customDamageResistanceTypes?.physical))) {
          totalMult = totalMult * mult;
          continue;
        } else if (magicalDamage && trait.has("magic")) {
          totalMult = totalMult * mult;
          continue;
        } else if (item?.type === "spell" && trait.has("spell")) {
          totalMult = totalMult * mult;
          continue;
        }
        if (customs.length > 0) {
          if (!magicalDamage && (customs.includes("nonmagic") || customs.includes(GameSystemConfig.customDamageResistanceTypes?.nonmagic))) {
            totalMult = totalMult * mult;
            continue;
          } else if (!magicalDamage && physicalDamage && (customs.includes("physical") || customs.includes(GameSystemConfig.customDamageResistanceTypes?.physical))) {
            totalMult = totalMult * mult;
            continue;
          } else if (magicalDamage && (customs.includes("magic") || customs.includes(GameSystemConfig.customDamageResistanceTypes.magic))) {
            totalMult = totalMult * mult;
            continue;
          } else if (item?.type === "spell" && (customs.includes("spell") || customs.includes(GameSystemConfig.customDamageResistanceTypes.spell))) {
            totalMult = totalMult * mult;
            continue;
          }
        }
        if (configSettings.damageImmunities === "immunityPhysical") {
          if (!magicalDamage && trait.has("physical"))
            physicalDamageTypes.forEach((dt) => trait.add(dt));
          if (!(magicalDamage || silverDamage) && trait.has("silver"))
            physicalDamageTypes.forEach((dt) => trait.add(dt));
          if (!(magicalDamage || adamantineDamage) && trait.has("adamant"))
            physicalDamageTypes.forEach((dt) => trait.add(dt));
        }
      }
      if (trait.has(dmgTypeString))
        totalMult = totalMult * mult;
    }
  }
  return totalMult;
}, "getTraitMult");
async function applyTokenDamage(damageDetail, totalDamage, theTargets, item, saves, options = {
  label: "defaultDamage",
  existingDamage: [],
  superSavers: /* @__PURE__ */ new Set(),
  semiSuperSavers: /* @__PURE__ */ new Set(),
  workflow: void 0,
  updateOptions: { awaitDamageApplication: configSettings.waitForDamageApplication },
  forceApply: false,
  noConcentrationCheck: false
}) {
  let allDamages = {};
  damageDetail = damageDetail.map((de) => ({ ...de, value: de.value ?? de.damage }));
  let workflow = options.workflow;
  if (item && !options.workflow) workflow = Workflow.getWorkflow(item.uuid);
  for (let token of theTargets) {
    const actor = token.actor;
    if (!actor) continue;
    const isHit = true;
    const saved = !!saves?.has(token);
    const superSaver = !!options.superSavers?.has(token);
    const semiSuperSaver = !!options.semiSuperSavers?.has(token);
    let saveMultiplier = 1;
    if (saved) {
      saveMultiplier = getSaveMultiplierForItem(item, "defaultDamage");
    }
    if (semiSuperSaver && saved) {
      saveMultiplier = 0;
    }
    const calcDamageOptions = {
      invertHealing: true,
      multiplier: 1,
      midi: {
        saved,
        itemType: item?.type,
        saveMultiplier,
        isHit: true,
        superSaver,
        semiSuperSaver,
        sourceActorUuid: actor?.uuid,
        // @ts-expect-error no dnd5e-types
        uncannyDodge: foundry.utils.getProperty(actor, `flags.${MODULE_ID}.uncanny-dodge`) && item?.hasAttack,
        // some options for ripper's module
        save: saved,
        targetUuid: token?.document.uuid,
        fumbleSave: false,
        criticalSave: false,
        isCritical: false,
        isFumble: false
      }
    };
    if (configSettings.saveDROrder === "DRSavedr") {
      calcDamageOptions.midi.saveMultiplier = saveMultiplier;
    } else {
      calcDamageOptions.midi.saveMultiplier = 1;
      calcDamageOptions.multiplier = saveMultiplier;
    }
    const combinedDamage = foundry.utils.deepClone(actor.calculateDamage(damageDetail, calcDamageOptions));
    const fullDamage = setupDamageDetails({
      actorId: actor.id,
      actorUuid: actor.uuid,
      critical: false,
      damageDetail: combinedDamage,
      damageDetails: {
        combinedDamage,
        rawcombinedDamage: damageDetail,
        calcDamageOptions: { combinedDamage: calcDamageOptions }
      },
      isHit,
      rawDamageDetail: damageDetail,
      saved,
      sceneId: canvas.scene?.id ?? "",
      semiSuperSaver,
      superSaver,
      totalDamage,
      targetUuid: token.document.uuid
    }, "combinedDamage", actor);
    if (fullDamage) allDamages[actor.uuid] = fullDamage;
  }
  if (!options.updateOptions?.awaitDamageApplication) {
    options.updateOptions ??= {};
    options.updateOptions.awaitDamageApplication = true;
  }
  const cardIds = await timedAwaitExecuteAsGM("createReverseDamageCard", {
    autoApplyDamage: configSettings.autoApplyDamage,
    sender: game.user?.name,
    actorId: workflow?.actor?.id,
    actorUuid: workflow?.actor?.uuid,
    charName: workflow?.token?.name ?? workflow?.actor?.name ?? game.user?.name,
    damageList: prepareDamageListToJSON(Object.values(allDamages)),
    chatCardUuid: workflow?.itemCardUuid,
    flagTags: workflow?.flagTags,
    updateOptions: options.updateOptions,
    forceApply: options.forceApply
  });
  return cardIds;
}
__name(applyTokenDamage, "applyTokenDamage");
async function applyTokenDamageMany(targetDamages, item, options = {
  workflow: void 0,
  updateOptions: { awaitDamageApplication: configSettings.waitForDamageApplication },
  forceApply: false
}) {
  let allDamages = {};
  let workflow = options.workflow;
  if (item && !options.workflow) workflow = Workflow.getWorkflow(item.uuid);
  for (const entry of targetDamages) {
    const actor = entry.token.actor;
    if (!actor) continue;
    const damageDetail = entry.damageDetail.map((de) => ({ ...de, value: de.value ?? de.damage }));
    const calcDamageOptions = {
      invertHealing: true,
      multiplier: 1,
      midi: {
        saved: false,
        itemType: item?.type,
        saveMultiplier: 1,
        isHit: true,
        superSaver: false,
        semiSuperSaver: false,
        sourceActorUuid: actor?.uuid,
        uncannyDodge: false,
        save: false,
        targetUuid: entry.token?.document.uuid,
        fumbleSave: false,
        criticalSave: false,
        isCritical: false,
        isFumble: false
      }
    };
    const combinedDamage = foundry.utils.deepClone(actor.calculateDamage(damageDetail, calcDamageOptions));
    const fullDamage = setupDamageDetails({
      actorId: actor.id,
      actorUuid: actor.uuid,
      critical: false,
      damageDetail: combinedDamage,
      damageDetails: {
        combinedDamage,
        rawcombinedDamage: damageDetail,
        calcDamageOptions: { combinedDamage: calcDamageOptions }
      },
      isHit: true,
      rawDamageDetail: damageDetail,
      saved: false,
      sceneId: canvas.scene?.id ?? "",
      semiSuperSaver: false,
      superSaver: false,
      totalDamage: entry.totalDamage,
      targetUuid: entry.token.document.uuid
    }, "combinedDamage", actor);
    if (fullDamage) allDamages[actor.uuid] = fullDamage;
  }
  if (!options.updateOptions?.awaitDamageApplication) {
    options.updateOptions ??= {};
    options.updateOptions.awaitDamageApplication = true;
  }
  const cardIds = await timedAwaitExecuteAsGM("createReverseDamageCard", {
    autoApplyDamage: configSettings.autoApplyDamage,
    sender: game.user?.name,
    actorId: workflow?.actor?.id,
    actorUuid: workflow?.actor?.uuid,
    charName: workflow?.token?.name ?? workflow?.actor?.name ?? game.user?.name,
    damageList: prepareDamageListToJSON(Object.values(allDamages)),
    chatCardUuid: workflow?.itemCardUuid,
    flagTags: workflow?.flagTags,
    updateOptions: options.updateOptions,
    forceApply: options.forceApply
  });
  return cardIds;
}
__name(applyTokenDamageMany, "applyTokenDamageMany");
function mergeDamageDetail(damageDetail) {
  const merged = {};
  for (const dd of damageDetail) {
    const makeHash = /* @__PURE__ */ __name((type, properties = [], active) => [type, ...true ? Array.from(properties).sort() : [], Array.from(active ? Object.entries(active) : [])].join(), "makeHash");
    const key = makeHash(dd.type, dd.properties ?? /* @__PURE__ */ new Set(), dd.active);
    if (dd.type === "midi-none") continue;
    if (merged[key]) {
      merged[key].value += dd.value;
      merged[key].damage = merged[key].value;
      merged[key].formula += " + " + dd.formula;
      if (dd.properties)
        merged[key].properties = merged[key].properties.union(dd.properties);
    } else {
      merged[key] = { ...dd };
    }
  }
  return Object.values(merged);
}
__name(mergeDamageDetail, "mergeDamageDetail");
function setupDamageDetails(actorDamage, selector, actor) {
  const mergedDetails = mergeDamageDetail(actorDamage.damageDetails[selector] ?? []);
  let { damage, temp, healing } = mergedDetails.reduce((acc, d) => {
    if (d.type === "temphp") acc.temp += d.value;
    else if (d.type === "healing") acc.healing += d.value;
    else if (!["midi-none", "vitality"].includes(d.type)) acc.damage += d.value;
    return acc;
  }, { damage: 0, temp: 0, healing: 0 });
  damage = Math.max(0, damage);
  let totalDamage = damage;
  let healingAdjustedTotalDamage = damage + healing;
  healingAdjustedTotalDamage = healingAdjustedTotalDamage < 0 ? Math.ceil(healingAdjustedTotalDamage) : Math.floor(healingAdjustedTotalDamage);
  const as = actor?.system;
  if (!as || !as.attributes.hp) return;
  let effectiveTemp = as.attributes.hp.temp ?? 0;
  const deltaTemp = healingAdjustedTotalDamage > 0 ? Math.min(effectiveTemp, healingAdjustedTotalDamage) : 0;
  const deltaHP = Math.clamp(healingAdjustedTotalDamage - deltaTemp, -as.attributes.hp.damage, as.attributes.hp.value);
  const oldTempHP = as.attributes.hp.temp ?? 0;
  const newTempHP = Math.floor(Math.max(0, effectiveTemp - deltaTemp, temp));
  const completeActorDamage = {
    damageSelector: selector,
    ...actorDamage,
    updateOptions: {},
    calcDamageOptions: actorDamage.damageDetails.calcDamageOptions[selector],
    // @ts-expect-error no dnd5e-types
    oldHP: as.attributes.hp.value,
    // @ts-expect-error no dnd5e-types
    newHP: as.attributes.hp.value - deltaHP,
    oldTempHP,
    useDamageDetail: configSettings.useDamageDetail,
    hpDamage: deltaHP,
    newTempHP,
    // damages.tempDamage = deltaTemp,
    tempDamage: oldTempHP - newTempHP,
    totalDamage,
    healingAdjustedTotalDamage,
    details: [],
    wasHit: actorDamage.isHit
  };
  return completeActorDamage;
}
__name(setupDamageDetails, "setupDamageDetails");
async function processDamageRoll(workflow, defaultDamageType) {
  if (debugEnabled > 0) warn("processDamageRoll |", workflow);
  let hitTargets = /* @__PURE__ */ new Set([...workflow.hitTargets ?? [], ...workflow.hitTargetsEC ?? []]);
  let theTargets = new Set(workflow.targets);
  let effectsToExpire = [];
  if (hitTargets.size > 0 && workflow.activity.attack) effectsToExpire.push("1Hit");
  if (hitTargets.size > 0 && workflow.activity.damage) effectsToExpire.push("DamageDealt");
  if (effectsToExpire.length > 0) {
    await expireMyEffects.bind(workflow)(effectsToExpire);
  }
  if (debugEnabled > 0) warn("processDamageRoll | damage details pre merge are ", workflow.rawDamageDetail, workflow.rawBonusDamageDetail ?? []);
  let totalDamage = 0;
  const baseNoDamage = workflow.rawDamageDetail?.length === 0 || workflow.rawDamageDetail?.length === 1 && workflow?.rawDamageDetail[0]?.type === "midi-none";
  const bonusNoDamage = workflow.rawBonusDamageDetail?.length === 0 || workflow.rawBonusDamageDetail?.length === 1 && workflow.rawBonusDamageDetail[0]?.type === "midi-none";
  const otherNoDamage = workflow.rawOtherDamageDetail?.length === 0 || workflow.rawOtherDamageDetail?.length === 1 && workflow.rawOtherDamageDetail[0]?.type === "midi-none";
  if (baseNoDamage && bonusNoDamage && otherNoDamage) return;
  const damagePerActor = {};
  workflow.damageList = [];
  totalDamage = 0;
  totalDamage = workflow.rawDamageDetail?.reduce((acc, di) => acc + (di.type === "temphp" ? 0 : di.type === "healing" ? -di.value : di.value), 0) ?? 0;
  if (workflow.rawOtherDamageDetail) totalDamage += workflow.rawOtherDamageDetail.reduce((acc, di) => acc + (di.type === "temphp" ? 0 : di.value), 0) ?? 0;
  if (workflow.rawBonusDamageDetail) totalDamage += workflow.rawBonusDamageDetail.reduce((acc, di) => acc + (di.type === "temphp" ? 0 : di.value), 0) ?? 0;
  const defaultSaveMultiplier = getSaveMultiplierForActivity(workflow.activity);
  for (let token of theTargets) {
    const actor = token.actor;
    if (!actor) continue;
    const tokenDocument = token.document;
    let challengeModeScale = 1;
    let challengeModeAR = 0;
    if (["scale", "scaleNoAR"].includes(checkRule("challengeModeArmor")) && workflow.attackRoll && workflow.hitTargetsEC?.has(token)) {
      const scale = workflow.challengeModeScale?.[tokenDocument?.uuid ?? "dummy"] ?? 1;
      challengeModeScale = scale;
    } else if (checkRule("challengeModeArmor") === "challenge" && workflow.hitTargetsEC?.has(token)) {
      challengeModeAR = token.actor?.system.attributes.ac.AR ?? 0;
    }
    const semiSuperSaver = workflow.semiSuperSavers.has(token);
    const superSaver = workflow.superSavers.has(token);
    const hasSaved = workflow.saves.has(token);
    const saveMultiplier = hasSaved ? superSaver || semiSuperSaver ? 0 : defaultSaveMultiplier : superSaver ? defaultSaveMultiplier : 1;
    damagePerActor[actor.uuid] = {
      actorId: actor.id,
      actorUuid: actor.uuid,
      challengeModeScale,
      challengeModeAR,
      critical: workflow.isCritical,
      damageDetail: [],
      damageDetails: { combinedDamage: [], rawcombinedDamage: [], defaultDamage: [], rawdefaultDamage: workflow.rawDamageDetail ?? [], otherDamage: [], rawotherDamage: workflow.rawOtherDamageDetail ?? [], bonusDamage: [], rawbonusDamage: workflow.rawBonusDamageDetail ?? [], calcDamageOptions: {} },
      isHit: hitTargets.has(token),
      rawDamageDetail: [],
      saved: hasSaved,
      sceneId: canvas.scene?.id ?? "",
      semiSuperSaver,
      superSaver,
      totalDamage,
      targetUuid: tokenDocument?.uuid
    };
    if (!foundry.utils.getProperty(workflow.activity.item, `flags.${MODULE_ID}.noProvokeReaction`) && !workflow?.workflowOptions?.noProvokeReaction) {
      if (totalDamage !== 0 && (workflow.hitTargets.has(token) || workflow.hitTargetsEC.has(token) || workflow.hasSave)) {
        const isHealing = "heal" === workflow.activity.actionType || totalDamage < 0;
        await doReactions(token, workflow.tokenUuid, workflow.damageRoll ?? workflow.bonusDamageRoll ?? workflow.otherDamageRoll ?? new Roll("0"), !isHealing ? "reactiondamage" : "reactionheal", { activity: workflow.activity, item: workflow.item, workflow, workflowOptions: { damageDetail: workflow.rawDamageDetail, damageTotal: totalDamage, sourceActorUuid: workflow.actor.uuid, sourceItemUuid: workflow.item.uuid, sourceAmmoUuid: workflow.ammunition?.uuid } });
      }
    }
    const damageDetails = damagePerActor[actor.uuid].damageDetails;
    let combinedRawDamageDetails = [];
    let combinedDamageDetails = [];
    const damageArr = [[workflow.damageDetail ?? [], "defaultDamage"], [workflow.otherDamageMatches?.has(token) ?? true ? workflow.otherDamageDetail ?? [] : [], "otherDamage"], [workflow.bonusDamageDetail ?? [], "bonusDamage"]];
    for (let [damages, type] of damageArr) {
      if (!damages) {
        const message = `processDamageRoll | ${type} damages is not defined`;
        TroubleShooter.recordError(new Error("no valid damage"), message);
        error2(message);
        continue;
      }
      let calcDamageOptions = {
        invertHealing: true,
        multiplier: challengeModeScale,
        // Always initialize ignore to ensure dnd5e calculateDamage processes traits correctly
        ignore: {
          immunity: /* @__PURE__ */ new Set(),
          resistance: /* @__PURE__ */ new Set(),
          vulnerability: /* @__PURE__ */ new Set(),
          modification: /* @__PURE__ */ new Set(),
          absorption: /* @__PURE__ */ new Set()
        },
        midi: {
          challengeModeAR,
          applyDamage: true,
          criticalSave: workflow.criticalSaves.has(token),
          fumbleSave: workflow.fumbleSaves.has(token),
          isCritical: workflow.isCritical,
          isFumble: workflow.isFumble,
          isHit: hitTargets.has(token),
          itemType: workflow.item.type,
          save: hasSaved,
          saved: hasSaved,
          saveMultiplier,
          // default
          semiSuperSaver,
          sourceActorUuid: workflow.actor.uuid,
          superSaver,
          targetUuid: token.document.uuid,
          // @ts-expect-error no dnd5e-types
          uncannyDodge: actor.flags?.[MODULE_ID]?.["uncanny-dodge"] && workflow.item?.hasAttack
        }
      };
      if (type === "otherDamage" && (workflow.otherActivity?.save || workflow.otherActivity?.check)) {
        const defaultSaveMultiplier2 = getSaveMultiplierForActivity(workflow.otherActivity);
        const saveMultiplier2 = hasSaved ? superSaver || semiSuperSaver ? 0 : defaultSaveMultiplier2 : superSaver ? defaultSaveMultiplier2 : 1;
        calcDamageOptions.midi.saveMultiplier = saveMultiplier2;
      }
      const categories = { "idi": "immunity", "idr": "resistance", "idv": "vulnerability", "ida": "absorption", "idm": "modification" };
      if (workflow?.activity) {
        const ignoreTraits = workflow.activity.midiProperties?.ignoreTraits;
        if (ignoreTraits) {
          for (let key of Object.keys(categories)) {
            const category = categories[key];
            if (ignoreTraits.has(key)) {
              if (!calcDamageOptions.ignore?.[category]) foundry.utils.setProperty(calcDamageOptions, `ignore.${category}`, /* @__PURE__ */ new Set());
              for (let dt of Object.keys(GameSystemConfig.damageTypes)) {
                calcDamageOptions.ignore[category].add(dt);
              }
            }
          }
          for (const trait of ignoreTraits) {
            if (trait.includes(".")) {
              const [categoryKey, damageType] = trait.split(".");
              const category = categories[categoryKey];
              if (category && GameSystemConfig.damageTypes[damageType]) {
                if (!calcDamageOptions.ignore?.[category]) foundry.utils.setProperty(calcDamageOptions, `ignore.${category}`, /* @__PURE__ */ new Set());
                calcDamageOptions.ignore[category].add(damageType);
              }
            }
          }
        }
      }
      let returnDamages = token.actor.calculateDamage(damages, calcDamageOptions);
      damageDetails[type] = returnDamages;
      damageDetails[`raw${type}`] = damages;
      damageDetails.calcDamageOptions[type] = calcDamageOptions;
      if (configSettings.singleConcentrationRoll) {
        combinedDamageDetails = combinedDamageDetails.concat(returnDamages);
        combinedRawDamageDetails = combinedRawDamageDetails.concat(damages);
      }
    }
    damageDetails.calcDamageOptions.combinedDamage = damageDetails.calcDamageOptions.defaultDamage;
    if (configSettings.singleConcentrationRoll) {
      damageDetails["rawcombinedDamage"] = mergeDamageDetail(combinedRawDamageDetails);
      damageDetails["combinedDamage"] = combinedDamageDetails;
    } else {
      damageDetails.rawcombinedDamage = mergeDamageDetail((damageDetails.rawdefaultDamage ?? []).concat(damageDetails.rawbonusDamage ?? []));
      damageDetails.combinedDamage = (damageDetails?.defaultDamage ?? []).concat(damageDetails.bonusDamage ?? []);
    }
  }
  const preDamageList = Object.values(damagePerActor);
  const toCheck = ["combinedDamage"];
  if (!configSettings.singleConcentrationRoll && workflow.otherDamageRolls) toCheck.push("otherDamage");
  let chatCardUuids = [];
  for (let selector of toCheck) {
    workflow.damageList = [];
    preDamageList.forEach((damageEntry) => {
      damageEntry.damageDetail = damageEntry.damageDetails[selector] ?? damageEntry.damageDetail;
      damageEntry.rawDamageDetail = damageEntry.damageDetails[`raw${selector}`] ?? damageEntry.rawDamageDetail;
    });
    for (let token of theTargets) {
      const actor = token.actor;
      if (!actor) continue;
      const preDamageDetails = damagePerActor[actor.uuid];
      const damageDetails = setupDamageDetails(preDamageDetails, selector, actor);
      if (damageDetails) {
        damageDetails.damageDetails[selector] = damageDetails.damageDetail;
        workflow.damageList.push(damageDetails);
        await workflow?.callDamageHooksAndMacros(damageDetails, token);
      }
    }
    await timedAwaitExecuteAsGM("createReverseDamageCard", {
      autoApplyDamage: configSettings.autoApplyDamage,
      sender: game.user?.name,
      actorId: workflow.actor.id,
      actorUuid: workflow.actor.uuid,
      charName: workflow.token?.name ?? workflow.actor.name ?? game.user?.name,
      damageList: prepareDamageListToJSON(workflow.damageList),
      chatCardUuid: workflow.itemCardUuid,
      flagTags: workflow.flagTags,
      updateOptions: { noConcentrationCheck: workflow?.workflowOptions?.noConcentrationCheck ?? false, awaitDamageApplication: configSettings.waitForDamageApplication },
      forceApply: false
    }).then((cardIds) => {
      if (cardIds) chatCardUuids.push(...cardIds);
      if (workflow && configSettings.undoWorkflow) {
        if (workflow.undoData) {
          workflow.undoData.chatCardUuids = workflow.undoData.chatCardUuids.concat(chatCardUuids);
          unTimedExecuteAsGM("updateUndoChatCardUuids", workflow.undoData);
        }
      }
    });
  }
  if (debugEnabled > 1) debug(`process damage roll complete for ${workflow.item.name} `, workflow.damageList);
}
__name(processDamageRoll, "processDamageRoll");
function getSaveMultiplierForActivity(activity) {
  if (!activity) {
    error2("getSaveMultiplierForActivity called with no activity");
    return 1;
  }
  if (activity?.damage?.onSave === void 0) return 1;
  let damageOnSave = activity.damage?.onSave;
  if (activity.item.type === "spell" && activity.item.system.level === 0 && damageOnSave === "none") {
    const midiFlags2 = activity.actor?.flags?.[MODULE_ID];
    if (midiFlags2?.potentCantrip) return configSettings.defaultSaveMultiplier;
  }
  switch (activity.damage.onSave) {
    case "half":
      return configSettings.defaultSaveMultiplier;
    case "none":
      return 0;
    case "full":
      return 1;
    default:
      return configSettings.defaultSaveMultiplier;
  }
}
__name(getSaveMultiplierForActivity, "getSaveMultiplierForActivity");
var getSaveMultiplierForItem = /* @__PURE__ */ __name((item, itemDamageType) => {
  console.warn("getSaveMultiplierForItem is deprecated, use getSaveMultiplierForActivity instead");
  if (!item) return 1;
  if (item.actor && item.type === "spell" && item.system.level === 0) {
    const midiFlags2 = item.actor?.flags?.[MODULE_ID];
    if (midiFlags2?.potentCantrip) return configSettings.defaultSaveMultiplier;
    return 0;
  }
  return configSettings.defaultSaveMultiplier;
}, "getSaveMultiplierForItem");
function getToolLabel(toolId) {
  const toolUUID = GameSystemConfig.tools[toolId]?.id;
  const toolIndex = toolUUID ? dnd5e.documents.Trait.getBaseItem(toolUUID, { indexOnly: true }) : null;
  return toolIndex?.name ?? toolId;
}
__name(getToolLabel, "getToolLabel");
function buildRollChoices(actor, rollType, ids) {
  if (rollType === "skill") {
    return ids.map((skillId) => {
      const label = GameSystemConfig.skills[skillId]?.label ?? skillId;
      const total = actor.system.skills?.[skillId]?.total ?? 0;
      const modifier = total >= 0 ? `+${total}` : `${total}`;
      return { value: skillId, label, modifier };
    });
  }
  if (rollType === "tool") {
    return ids.map((toolId) => {
      const label = getToolLabel(toolId);
      const total = actor.system.tools?.[toolId]?.total ?? 0;
      const modifier = total >= 0 ? `+${total}` : `${total}`;
      return { value: toolId, label, modifier };
    });
  }
  return ids.map((abilityId) => {
    const label = GameSystemConfig.abilities[abilityId]?.label ?? abilityId;
    let total;
    const abilityData = actor.system.abilities?.[abilityId];
    if (rollType === "save") {
      total = abilityData?.save?.value ?? abilityData?.mod ?? 0;
    } else {
      total = (abilityData?.mod ?? 0) + (abilityData?.checkBonus ?? 0);
    }
    const modifier = total >= 0 ? `+${total}` : `${total}`;
    return { value: abilityId, label, modifier };
  });
}
__name(buildRollChoices, "buildRollChoices");
function selectBestRollChoice(rollChoices, advantageMap) {
  if (rollChoices.length === 0) return void 0;
  if (rollChoices.length === 1) return rollChoices[0];
  const getEffectiveValue = /* @__PURE__ */ __name((choice) => {
    const mod = parseInt(choice.modifier) || 0;
    const advData = advantageMap?.[choice.value];
    if (advData?.hasAdvantage && !advData?.hasDisadvantage) return mod + 3.325;
    if (advData?.hasDisadvantage && !advData?.hasAdvantage) return mod - 3.325;
    return mod;
  }, "getEffectiveValue");
  return rollChoices.reduce((a, b) => getEffectiveValue(b) > getEffectiveValue(a) ? b : a);
}
__name(selectBestRollChoice, "selectBestRollChoice");
function requestPCSave(ability, rollType, player, actor, options) {
  try {
    const actorName = actor?.name ?? "Unknown";
    let abilityString;
    const rollAbilities = options.rollAbilities;
    const rollSkills = options.rollSkills;
    const rollTools = options.rollTools;
    if (rollTools && rollTools.length > 0) {
      const labels = rollTools.map((t) => getToolLabel(t));
      abilityString = labels.join(` ${i18n("midi-qol.or")} `);
    } else if (rollSkills && rollSkills.length > 0) {
      const labels = rollSkills.map((s) => GameSystemConfig.skills[s]?.label ?? s);
      abilityString = labels.join(` ${i18n("midi-qol.or")} `);
    } else if (rollAbilities && rollAbilities.length > 0) {
      const labels = rollAbilities.map((a) => GameSystemConfig.abilities[a]?.label ?? a);
      abilityString = labels.join(` ${i18n("midi-qol.or")} `);
    } else {
      abilityString = ability || i18n("midi-qol.plain-d20-roll");
      let abilityDetails = GameSystemConfig.abilities[ability];
      if (!abilityDetails) abilityDetails = { ...GameSystemConfig.skills[ability], label: "" };
      if (!abilityDetails) abilityDetails = { ...GameSystemConfig.tools[ability], label: "" };
      if (abilityDetails?.label) abilityString = abilityDetails.label;
    }
    const rollLabel = rollType === "tool" ? i18n("midi-qol.tool-check") : rollType === "skill" ? i18n("midi-qol.skill-check") : rollType === "check" ? i18n("midi-qol.ability-check") : i18n("midi-qol.saving-throw");
    let content = ` ${actorName} ${shouldDisplaySaveDC() ? "DC " + options.saveDetails.rollDC : ""} ${abilityString} ${rollLabel}`;
    if (options.saveDetails.advantage && !options.saveDetails.disadvantage) content = content + ` (${i18n("DND5E.Advantage")}) - ${options.flavor})`;
    else if (!options.saveDetails.advantage && options.saveDetails.disadvantage) content = content + ` (${i18n("DND5E.Disadvantage")}) - ${options.flavor})`;
    else content = content + ` - ${options.flavor})`;
    const chatData = {
      content,
      whisper: [player]
    };
    return ChatMessage.create(chatData);
  } catch (err) {
    const message = `midi-qol | request PC save`;
    TroubleShooter.recordError(err, message);
    error2(message, err);
    return void 0;
  }
}
__name(requestPCSave, "requestPCSave");
function requestPCActiveDefence(player, actor, saveItemName, requestId, options) {
  const useUuid = true;
  const actorId = useUuid ? actor.uuid : actor.id;
  let rollMode = checkRule("activeDefenceShow") ?? CONST.DICE_ROLL_MODES.SELF;
  let flavor = `${saveItemName} ${configSettings.optionalRules.activeDefence ? "DC " + options.rollOptions.target : ""} ${i18n("midi-qol.ActiveDefenceString")}`;
  const midiType = "defenceRoll";
  socketlibSocket.executeAsUser("D20Roll", player.id, { targetUuid: actor.uuid, formula: options.formula, bonus: options.bonus, coverBonus: options.coverBonus, rollOptions: options.rollOptions, rollMode, midiType, flavor, messageData: { speaker: getSpeaker(actor) } }).then((result) => {
    if (debugEnabled > 1) debug("D20Roll result ", result);
    log("midi-qol | D20Roll result ", result);
    const handler = options.workflow.defenceRequests[requestId];
    if (!handler)
      return;
    delete options.workflow.defenceRequests[requestId];
    delete options.workflow.defenceTimeouts[requestId];
    let returnValue;
    try {
      returnValue = CONFIG.Dice.D20Roll.fromJSON(JSON.stringify(result));
    } catch (err) {
      returnValue = {};
    }
    handler(returnValue);
  });
}
__name(requestPCActiveDefence, "requestPCActiveDefence");
var midiFlagTypes = {};
function setupMidiFlagTypes() {
  let attackTypes = allAttackTypes.concat(["heal", "other", "save", "util"]);
  attackTypes.forEach((at) => {
    midiFlagTypes[`flags.midi-qol.DR.${at}`] = "number";
  });
  midiFlagTypes["flags.midi-qol.onUseMacroName"] = "string";
  if (game.system?.id === "dnd5e") {
    midiFlagTypes[`flags.midi-qol.DR.all`] = "string";
    midiFlagTypes[`flags.midi-qol.DR.non-magical`] = "string";
    midiFlagTypes[`flags.midi-qol.DR.non-silver`] = "string";
    midiFlagTypes[`flags.midi-qol.DR.non-adamant`] = "string";
    midiFlagTypes[`flags.midi-qol.DR.non-physical`] = "string";
    midiFlagTypes[`flags.midi-qol.DR.final`] = "number";
    Object.keys(GameSystemConfig.damageTypes).forEach((dt) => {
      midiFlagTypes[`flags.midi-qol.DR.${dt}`] = "string";
    });
  }
}
__name(setupMidiFlagTypes, "setupMidiFlagTypes");
function midiCustomEffect(actor, change, current, delta, changes) {
  if (!change.key) return true;
  if (typeof change?.key !== "string") return true;
  const originUuid = change.effect?.transfer ? change.effect.parent?.uuid : change.effect?.origin;
  if (!change.key?.startsWith(`flags.${MODULE_ID}`) && !change.key?.startsWith("system.traits.da.")) return true;
  const deferredEvaluation = [
    `flags.${MODULE_ID}.OverTime`,
    `flags.${MODULE_ID}.optional`,
    `flags.${MODULE_ID}.advantage`,
    `flags.${MODULE_ID}.disadvantage`,
    `flags.${MODULE_ID}.superSaver`,
    `flags.${MODULE_ID}.semiSuperSaver`,
    `flags.${MODULE_ID}.grants`,
    `flags.${MODULE_ID}.fail`,
    `flags.${MODULE_ID}.max.damage`,
    `flags.${MODULE_ID}.min.damage`,
    `flags.${MODULE_ID}.critical`,
    `flags.${MODULE_ID}.noCritical`,
    `flags.${MODULE_ID}.ignoreCover`,
    `flags.${MODULE_ID}.ignoreWalls`,
    `flags.${MODULE_ID}.rangeOverride`
  ];
  if (change.key === "flags.dnd5e.DamageBonusMacro") {
  } else if (change.key === `flags.${MODULE_ID}.onUseMacroName`) {
    const args = change.value.split(",")?.map((arg) => arg.trim());
    const currentFlag = foundry.utils.getProperty(actor, `flags.${MODULE_ID}.onUseMacroName`) ?? "";
    if (args[0] === "ActivityMacro" || args[0] === MQActivityMacroLabel) {
      if (change.effect?.flags.dae?.activity) args[0] = `ActivityMacro.${change.effect.flags.dae.activity}`;
      if (change.effect.transfer) args[0] = `ActivityMacro.${change.effect.parent.system.activities.contents[0].uuid}`;
      else {
        const origin = fromUuidSync(change.effect?.origin);
        if (origin instanceof Item) {
          const activities = origin.system.activities?.contents;
          if (activities[0]?.uuid) args[0] = `ActivityMacro.${activities[0].uuid}`;
        } else if (origin instanceof ActiveEffect) {
          const activities = origin.parent?.system?.activities?.contents;
          if (activities[0]?.uuid) args[0] = `ActivityMacro.${activities[0].uuid}`;
        } else if (origin?.item) {
          args[0] = `ActivityMacro.${change.effect?.origin}`;
        }
      }
    } else if (args[0].startsWith("ActivityMacro") || args[0].startsWith(MQActivityMacroLabel)) {
      const potentialUuid = args[0].split(".").slice(1).join(".");
      if (potentialUuid.includes("Activity.")) {
      } else {
        const item = fromUuidSync(potentialUuid);
        if (item instanceof Item) {
          const activities = item.system.activities?.contents;
          if (activities[0]?.uuid) args[0] = `ActivityMacro.${activities[0].uuid}`;
        } else {
          const origin = fromUuidSync(change.effect?.origin);
          if (change.effect?.flags.dae?.activity) {
            const activities = fromUuidSync(change.effect.flags.dae.activity).item.system.activities;
            const activity = activities.find((a) => a.name === potentialUuid || a.identifier === potentialUuid);
            if (activity?.uuid) args[0] = `ActivityMacro.${activity.uuid}`;
          } else if (origin instanceof Item) {
            const activities = origin.system.activities?.contents;
            const activity = activities.find((a) => a.name === potentialUuid || a.identifier === potentialUuid);
            if (activity?.uuid) args[0] = `ActivityMacro.${activity.uuid}`;
          }
        }
      }
    } else if (args[0] === "ItemMacro" || args[0] === MQItemMacroLabel) {
      if (change.effect?.transfer) args[0] = `ItemMacro.${change.effect.parent?.uuid}`;
      else {
        if (originUuid?.includes("Item.")) {
          args[0] = `ItemMacro.${originUuid}`;
        } else {
          const origin = fromUuidSync(change.effect?.origin);
          if (origin instanceof Item) args[0] = `ItemMacro.${origin.uuid}`;
          else if (origin instanceof ActiveEffect) args[0] = `ItemMacro.${origin.origin}`;
        }
      }
    }
    if (originUuid?.includes("Item.")) {
      args[0] = `${args[0]}|${originUuid}`;
    }
    const extraFlag = `[${args[1]}]${args[0]}`;
    const macroString = currentFlag?.length > 0 ? [currentFlag, extraFlag].join(",") : extraFlag;
    foundry.utils.setProperty(actor, `flags.${MODULE_ID}.onUseMacroName`, macroString);
    return true;
  } else if (change.key.startsWith(`flags.${MODULE_ID}.optional.`) && (change.value.trim() === "ItemMacro" || change.value.trim() === MQItemMacroLabel)) {
    let macroString = change.value;
    if (originUuid?.includes("Item.")) {
      const itemUuid = originUuid?.includes("ActiveEffect.") ? originUuid.split(".").slice(0, -2).join(".") : originUuid;
      macroString = `ItemMacro.${itemUuid}`;
    } else {
      const origin = fromUuidSync(change.effect?.origin);
      if (origin instanceof Item) macroString = `ItemMacro.${origin.uuid}`;
      else if (origin instanceof ActiveEffect) macroString = `ItemMacro.${origin.origin}`;
    }
    foundry.utils.setProperty(actor, change.key, macroString);
    return true;
  } else if (deferredEvaluation.some((k) => change.key.startsWith(k))) {
    if (typeof change.value !== "string") foundry.utils.setProperty(actor, change.key, change.value);
    else if (["true", "1"].includes(change.value.trim())) foundry.utils.setProperty(actor, change.key, true);
    else if (["false", "0"].includes(change.value.trim())) foundry.utils.setProperty(actor, change.key, false);
    else foundry.utils.setProperty(actor, change.key, change.value);
  } else if (change.key.match(/system.traits.*custom/)) {
  } else if (typeof change.value === "string" && change.key.startsWith("flags.midi-qol")) {
    let val;
    try {
      switch (midiFlagTypes[change.key]) {
        case "string":
          val = change.value;
          break;
        case "number":
          val = Number.isNumeric(change.value) ? JSON.parse(change.value) : 0;
          break;
        default:
          val = evalCondition(change.value, actor.getRollData(), { async: false });
      }
      if (debugEnabled > 0) warn("midiCustomEffect | setting ", change.key, " to ", val, " from ", change.value, " on ", actor.name);
      foundry.utils.setProperty(actor, change.key, val);
      foundry.utils.setProperty(actor, change.key.replace(`flags.${MODULE_ID}`, `flags.${MODULE_ID}.evaluated`), { value: val, effects: [change.effect?.name] });
    } catch (err) {
      const message = `midi-qol | midiCustomEffect | custom flag eval error ${change.key} ${change.value}`;
      TroubleShooter.recordError(err, message);
      console.warn(message, err);
    }
  }
  return true;
}
__name(midiCustomEffect, "midiCustomEffect");
function untargetDeadTokens() {
  if (autoRemoveTargets !== "none") {
    game.user?.targets.forEach((t) => {
      if (t.actor?.system.attributes.hp.value <= 0) {
        t.setTarget(false, { releaseOthers: false });
      }
    });
  }
}
__name(untargetDeadTokens, "untargetDeadTokens");
function replaceAtFields(value, context, options = { blankValue: "", maxIterations: 4 }) {
  if (typeof value !== "string") return value;
  let count = 0;
  if (!value.includes("@")) return value;
  let re = /@[\w\._\-]+/g;
  let result = foundry.utils.duplicate(value);
  result = result.replace("@item.level", "@itemLevel");
  result = result.replace(`@flags.${MODULE_ID}`, "@flags.midiqol");
  do {
    count += 1;
    for (let match of result.match(re) || []) {
      result = result.replace(match.replace("@data.", "@"), foundry.utils.getProperty(context, match.slice(1)) ?? options.blankValue);
    }
  } while (count < options.maxIterations && result.includes("@"));
  return result;
}
__name(replaceAtFields, "replaceAtFields");
async function doOverTimeEffect(actor, effect, startTurn = true, options = { saveToUse: void 0, rollFlags: void 0, isActionSave: false }) {
  if (game.user?.isGM)
    return gmOverTimeEffect(actor, effect, startTurn, options);
  else
    return unTimedExecuteAsGM("gmOverTimeEffect", { actorUuid: actor.uuid, effectUuid: effect.uuid, startTurn, options });
}
__name(doOverTimeEffect, "doOverTimeEffect");
async function doActivityOverTimeEffect(actor, effect, startTurn = true, options = { saveToUse: void 0, rollFlags: void 0, isActionSave: false }) {
  if (effect.disabled || effect.isSuppressed) return;
  const auraFlags = effect.flags?.ActiveAuras ?? {};
  if (auraFlags.isAura && auraFlags.ignoreSelf) return;
  const ROLL_MODES = CONST.DICE_ROLL_MODES;
  const owner = playerForActor(actor);
  const changes = effect.changes;
  let requiresUserExecution = false;
  for (let change of changes) {
    const activity = await getOvertimeActivity(actor, effect, change, startTurn);
    if (!activity) continue;
    if ([ROLL_MODES.PRIVATE, ROLL_MODES.SELF].includes(activity.midiProperties?.rollMode ?? "")) {
      requiresUserExecution = owner !== void 0;
      break;
    }
  }
  if (!requiresUserExecution || !owner || owner === game.user || !owner.active)
    return localActivityOverTimeEffect(actor, effect, startTurn, options);
  else {
    return socketlibSocket.executeAsUser("localActivityOverTimeEffect", owner.id, { actorUuid: actor.uuid, effectUuid: effect.uuid, startTurn, options });
  }
}
__name(doActivityOverTimeEffect, "doActivityOverTimeEffect");
async function getOvertimeActivity(actor, effect, change, startTurn) {
  const changeValue = String(change.value ?? "");
  let activity = await fromUuid(changeValue);
  if (!activity && effect.transfer) {
    activity = effect.parent?.system.activities?.find((a) => a.identifier === changeValue);
  }
  if (!activity && effect.origin) {
    const originItem = getItemFromEffectOrigin(effect.origin);
    if (originItem) {
      activity = originItem.system.activities.find((a) => a.identifier === changeValue);
    }
  }
  if (!activity) {
    console.error(`getOvertimeActivity | activity not found for changeValue: "${changeValue}"`);
    return void 0;
  }
  const turnChoice = activity.overTimeProperties?.turnChoice ?? "start";
  console.error(`getOvertimeActivity | found activity: ${activity.name}, turnChoice: ${turnChoice}, startTurn: ${startTurn}`);
  if (turnChoice === "start" && !startTurn) return void 0;
  if (turnChoice === "end" && startTurn) return void 0;
  return activity;
}
__name(getOvertimeActivity, "getOvertimeActivity");
async function localActivityOverTimeEffect(actor, effect, startTurn = true, options = { saveToUse: void 0, rollFlags: void 0, rollMode: void 0 }) {
  let changes = effect.changes.filter((change) => change.key.startsWith(`flags.${MODULE_ID}.ActivityOverTime`));
  changes = changes.sort((c1, c2) => (c1.priority ?? 10) - (c2.priority ?? 10));
  const castLevel = effect.flags?.[MODULE_ID]?.castData?.castLevel ?? 0;
  const spellLevel = effect.flags?.[MODULE_ID]?.castData?.baseLevel ?? 0;
  for (let change of changes) {
    const activity = await getOvertimeActivity(actor, effect, change, startTurn);
    if (!activity) {
      console.warn(`midi-qol | localActivityOverTime | for actor ${actor} activity ${change.value} not found or not overTime`);
      continue;
    }
    const rollAsChoice = activity.overTimeProperties?.rollAs ?? "target";
    let rollActor = actor;
    if (rollAsChoice === "source") {
      const originItem = getItemFromEffectOrigin(effect.origin ?? "");
      if (originItem?.actor) rollActor = originItem.actor;
    }
    const itemData = activity.item.toObject();
    if (castLevel > spellLevel) foundry.utils.setProperty(itemData, "flags.dnd5e.scaling", castLevel - spellLevel);
    foundry.utils.setProperty(itemData, "system.properties", itemData.system.properties.filter((p) => p !== "concentration"));
    foundry.utils.setProperty(itemData, "flags.midi-qol.syntheticItem", true);
    const newItem = new CONFIG.Item.documentClass(itemData, { parent: rollActor });
    newItem.prepareData();
    newItem.prepareFinalAttributes();
    let overTimeActivity = newItem.system.activities.find((a) => a.id === activity.id);
    if ((overTimeActivity.overTimeProperties?.preRemoveConditionText ?? "") !== "" && overTimeActivity.overTimeProperties.removeConditionBeforeActivity) {
      const conditionData = createConditionData({ actor, workflow: void 0, target: actor, effect });
      if (await evalCondition(activity.overTimeProperties?.preRemoveConditionText ?? "false", conditionData, { async: true })) {
        await effect.delete();
        return;
      }
    }
    if ((overTimeActivity?.useCondition ?? "") !== "") {
      const conditionData = createConditionData({ actor, workflow: void 0, target: actor, effect });
      if (!await evalCondition(activity.useCondition ?? "true", conditionData, { async: true, errorReturn: true })) {
        return;
      }
    }
    let targetsToUse = void 0;
    const checkGMstatus = rollAsChoice === "source" && rollActor !== actor && !rollActor.isOwner;
    const token = tokenForActor(actor);
    if (token) targetsToUse = /* @__PURE__ */ new Set([token]);
    const overTimeWorkflow = await completeActivityUse2(
      overTimeActivity,
      { midiOptions: { targetsToUse, checkGMstatus, workflowOptions: { noProvokeReaction: true, autoConsumeResource: "both" } } },
      { configure: overTimeActivity.consumption?.spellSlot === true },
      { create: true }
    );
    if (activity.overTimeProperties?.saveRemoves && overTimeWorkflow?.saves.has(getOrCreateTokenForActor(actor))) {
      await effect.delete();
      return;
    }
    if ((activity.overTimeProperties?.postRemoveConditionText ?? "") !== "" && !activity.overTimeProperties.removeConditionBeforeActivity) {
      const conditionData = createConditionData({ actor, workflow: overTimeWorkflow, target: actor, effect });
      if (await evalCondition(activity.overTimeProperties.postRemoveConditionText ?? "false", conditionData, { async: true })) {
        await effect.delete();
        return;
      }
    }
  }
}
__name(localActivityOverTimeEffect, "localActivityOverTimeEffect");
async function resolveItemMacro(macroSpec, context) {
  const { actor, originItem } = context;
  let item;
  if (macroSpec === "ItemMacro" || macroSpec === MQItemMacroLabel) {
    item = originItem;
  } else {
    const itemSpec = macroSpec.replace(/^(ItemMacro|midi-qol\.ItemMacroText)\./, "").trim();
    const resolved = await fromUuid(itemSpec);
    if (resolved instanceof Item) {
      item = resolved;
    } else if (resolved instanceof ActiveEffect && resolved.parent instanceof Item) {
      item = resolved.parent;
    } else if (resolved?.item) {
      item = resolved.item;
    }
    if (!item && actor) {
      item = actor.items.find((i) => i.name === itemSpec && !!(i.flags?.dae?.macro ?? i.flags?.itemacro?.macro));
    }
  }
  if (!item) return void 0;
  const macroData = item.flags?.dae?.macro ?? item.flags?.itemacro?.macro;
  if (!macroData) return void 0;
  return { item, macroData };
}
__name(resolveItemMacro, "resolveItemMacro");
async function resolveActivityMacro(macroSpec, context) {
  const { originItem, originActivity } = context;
  let item = originItem;
  let activity;
  if (macroSpec === "ActivityMacro" || macroSpec === MQActivityMacroLabel) {
    activity = originActivity;
    item = originItem;
  } else if (macroSpec.startsWith("ActivityMacro-")) {
    const activityId = macroSpec.split("-").slice(1).join("-");
    if (originActivity?.id === activityId) {
      activity = originActivity;
    } else if (originItem) {
      activity = originItem.system.activities?.find((a) => a.id === activityId);
    }
    item = originItem;
  } else {
    const activitySpec = macroSpec.replace(/^(ActivityMacro|midi-qol\.ActivityMacroText)\./, "").trim();
    const resolved = await fromUuid(activitySpec);
    if (resolved instanceof Item) {
      item = resolved;
      activity = item.system.activities?.contents[0];
    } else if (resolved?.item) {
      activity = resolved;
      item = resolved.item;
    }
    if (!activity && originItem) {
      activity = originItem.system.activities?.find(
        (a) => a.identifier === activitySpec || a.id === activitySpec || a.name === activitySpec
      );
      item = originItem;
    }
  }
  if (!activity || !item) return void 0;
  const macro = activity.macro;
  if (!macro?.command) return void 0;
  return { item, activity, macro };
}
__name(resolveActivityMacro, "resolveActivityMacro");
function resolveDAEItemData(actor, uuid) {
  let itemId;
  if (uuid.includes("Activity.") || uuid.includes("ActiveEffect.")) {
    itemId = uuid.split(".").slice(-3)[0];
  } else {
    itemId = uuid.split(".").slice(-1)[0];
  }
  return actor.effects.find((e) => e.flags?.dae?.itemData?._id === itemId)?.flags?.dae?.itemData;
}
__name(resolveDAEItemData, "resolveDAEItemData");
function parseBool(value, defaultValue) {
  if (value === void 0) return defaultValue;
  return value === "true" || value === "1";
}
__name(parseBool, "parseBool");
function parseCountAction(value, defaultMode) {
  let consecutive = false;
  let working = value;
  const digitEnd = working.search(/\D/);
  if (digitEnd > 0 && working[digitEnd] === "c") {
    consecutive = true;
    working = working.substring(0, digitEnd) + working.substring(digitEnd + 1);
  }
  const minusIndex = working.indexOf("-");
  const plusIndex = working.indexOf("+");
  let actionIndex = -1;
  let mode = defaultMode;
  if (minusIndex > 0 && (plusIndex === -1 || minusIndex < plusIndex)) {
    actionIndex = minusIndex;
    mode = "remove";
  } else if (plusIndex > 0) {
    actionIndex = plusIndex;
    mode = "permanent";
  }
  if (actionIndex === -1) {
    return { count: Number(working), mode: defaultMode, consecutive };
  }
  const count = Number(working.substring(0, actionIndex));
  const effectSpec = working.substring(actionIndex + 1) || void 0;
  return { count, mode, effectSpec, consecutive };
}
__name(parseCountAction, "parseCountAction");
function countActionToString(action) {
  const base = (action.consecutive ? "c" : "") + (action.mode === "remove" ? "-" : "+");
  return action.effectSpec ? `${base}${action.effectSpec}` : base;
}
__name(countActionToString, "countActionToString");
async function handleActionSaveDialog(ctx) {
  const { actor, effect, details, owner } = ctx;
  const { saveDC, saveAbility, saveMagic, rollMode, rollTypeString } = details;
  if (!owner?.active) {
    error2(`No active owner to request overtime save for ${actor.name} ${effect.name}`);
    return false;
  }
  const saveDetails = {
    actorUuid: actor.uuid,
    advantage: void 0,
    disadvantage: void 0,
    modifierTracker: void 0,
    isFriendly: false,
    isMagicSave: saveMagic,
    isConcentrationCheck: false,
    rollDC: saveDC,
    saveItemUuid: "",
    workflowId: "",
    itemCardUuid: "",
    workflowOptions: {},
    rollType: rollTypeString,
    rollAbilities: rollTypeString !== "skill" ? saveAbility : [],
    rollSkills: rollTypeString === "skill" ? saveAbility : [],
    rollTools: [],
    itemId: ""
  };
  let saveResult = await socketlibSocket.executeAsUser("rollActionSave", owner?.id, {
    actorUuid: actor.uuid,
    effectName: effect.name,
    saveDetails,
    displayOptions: {
      fastForward: false,
      chatMessage: true,
      showTargetDC: true,
      rollMode
    }
  });
  const D20Roll = CONFIG.Dice.D20Roll;
  let roll;
  if (saveResult instanceof Array) {
    roll = saveResult[0] instanceof Roll ? saveResult[0] : D20Roll.fromJSON(JSON.stringify(saveResult[0]));
  } else if (saveResult && !(saveResult instanceof Roll)) {
    roll = D20Roll.fromJSON(JSON.stringify(saveResult));
  } else {
    roll = saveResult;
  }
  const success = roll?.total !== void 0 && roll.total >= saveDC;
  foundry.utils.setProperty(effect, `flags.${MODULE_ID}.actionSaveSuccess`, success === true);
  return true;
}
__name(handleActionSaveDialog, "handleActionSaveDialog");
async function handleActionSaveRollResponse(ctx, options, rollType) {
  const { actor, effect, details } = ctx;
  const { saveAbility, saveDC } = details;
  if (!options.rollFlags) return effect.id;
  if (options.rollFlags.type === "ability") options.rollFlags.type = "check";
  if (!rollType.includes(options.rollFlags.type) || !saveAbility.includes(options.rollFlags.ability ?? options.rollFlags.skillId)) {
    return null;
  }
  const success = options.saveToUse?.options?.success || options.saveToUse?.total >= saveDC || checkRule("criticalSaves") && options.saveToUse.isCritical;
  if (success !== void 0) {
    const chatCardUuids = effect.getFlag(MODULE_ID, "overtimeChatCardUuids");
    for (let chatcardUuid of chatCardUuids ?? []) {
      const chatCard = fromUuidSync(chatcardUuid);
      await chatCard?.delete();
    }
  }
  if (success) {
    await expireEffects(actor, [effect], { "expiry-reason": "midi-qol:overTime:actionSave" });
    return effect.id;
  } else {
    await effect.setFlag(MODULE_ID, "actionSaveSuccess", success === true);
  }
  return effect.id;
}
__name(handleActionSaveRollResponse, "handleActionSaveRollResponse");
async function handleActionSaveRollRequest(ctx, changeTurnEnd) {
  const { actor, effect, details, owner, messageFlavor } = ctx;
  const { saveAbility, saveDC, rollMode, label, rollTypeString } = details;
  const MessageClass = getDocumentClass("ChatMessage");
  let dataset;
  const chatCardUuids = [];
  for (let ability of saveAbility) {
    dataset = {
      type: rollTypeString,
      dc: saveDC,
      item: effect.name,
      action: "rollRequest",
      midiOvertimeActorUuid: actor.uuid,
      midiRollMode: rollMode
    };
    if (["check", "save"].includes(rollTypeString)) dataset.ability = ability;
    else if (rollTypeString === "skill") dataset.skill = ability;
    let whisper = ChatMessage.getWhisperRecipients(owner?.name ?? "");
    if (owner?.isGM) {
      whisper = ChatMessage.getWhisperRecipients("GM");
    }
    const buttonDataset = { ...dataset, action: "rollRequest", visibility: "all" };
    const chatData = {
      user: game.user?.id,
      whisper: whisper.map((u) => u.id ?? ""),
      rollMode: rollMode ?? "public",
      content: await foundry.applications.handlebars.renderTemplate("systems/dnd5e/templates/chat/roll-request-card.hbs", {
        buttons: [{
          // @ts-expect-error no dnd5e-types
          buttonLabel: game.system.enrichers.createRollLabel({ ...dataset, format: "long", icon: true, hideDC: !owner?.isGM && !shouldDisplaySaveDC() }),
          // @ts-expect-error no dnd5e-types
          hiddenLabel: game.system.enrichers.createRollLabel({ ...dataset, format: "long", icon: true, hideDC: true }),
          dataset: buttonDataset
        }]
      }),
      flavor: `Action: ${label ?? effect.name} ${i18n(messageFlavor[details.rollType])}`,
      speaker: MessageClass.getSpeaker({ actor })
    };
    const chatCard = await ChatMessage.create(chatData);
    if (chatCard) {
      chatCardUuids.push(chatCard.uuid);
      await chatCard?.setFlag(MODULE_ID, "actorUuid", actor.uuid);
    }
  }
  foundry.utils.setProperty(effect, `flags.${MODULE_ID}.actionSaveSuccess`, void 0);
  await effect.setFlag(MODULE_ID, "overtimeChatCardUuids", chatCardUuids);
  await effect.setFlag(MODULE_ID, "actionSaveSuccess", void 0);
  if (changeTurnEnd) return effect.id;
  return void 0;
}
__name(handleActionSaveRollRequest, "handleActionSaveRollRequest");
async function buildOvertimeItemData(ctx) {
  const { actor, effect, details, label, isPermanent } = ctx;
  const { damageRoll, damageType, damageBeforeSave, saveMagic, itemName, chatFlavor, saveDamage } = details;
  let itemData = {};
  itemData.img = "icons/svg/aura.svg";
  if (typeof itemName === "string") {
    let theItem = await fromUuid(itemName);
    if (!theItem && itemName.startsWith("Actor.")) {
      const localName = itemName.replace("Actor.", "");
      theItem = actor.items.getName(localName);
    }
    if (!theItem) {
      theItem = game.items?.getName(itemName);
    }
    if (theItem) itemData = theItem.toObject();
  }
  let activityData = {
    name: label,
    id: "overtime",
    type: "damage"
  };
  if (damageRoll) {
    activityData.damage = {
      parts: [{
        custom: {
          enabled: true,
          formula: damageRoll
        },
        types: [damageType]
      }]
    };
  }
  activityData.img = effect.img;
  itemData.img = effect.img;
  foundry.utils.setProperty(itemData, "target.affects.type", "self");
  itemData.type = "feat";
  foundry.utils.setProperty(itemData, "system.type.value", "feat");
  foundry.utils.setProperty(itemData, `flags.${MODULE_ID}.noProvokeReaction`, true);
  if (saveMagic) {
    itemData.type = "spell";
    foundry.utils.setProperty(itemData, "system.preparation", { mode: "atwill" });
  }
  if (!isPermanent) {
    const rollActivityData = buildRollActivityData(details);
    Object.assign(activityData, rollActivityData);
  }
  if (activityData.damage) {
    if (damageBeforeSave || saveDamage === "fulldamage") {
      activityData.damage.onSave = "full";
    } else if (saveDamage === "halfdamage") {
      activityData.damage.onSave = "half";
    } else {
      activityData.damage.onSave = "none";
    }
  }
  itemData.name = label;
  activityData.description = { chat: chatFlavor };
  foundry.utils.setProperty(itemData, "system.description.chat", effect.description ?? "");
  itemData._id = foundry.utils.randomID();
  return { itemData, activityData };
}
__name(buildOvertimeItemData, "buildOvertimeItemData");
function parseOvertimeDetails(specString, rollData, effectName) {
  let spec = replaceAtFields(specString, rollData, { blankValue: 0, maxIterations: 3 });
  spec = spec.replace(/\s*=\s*/g, "=").replace(/\s*,\s*/g, ",").replace("\n", "");
  let parts;
  if (spec.includes("#")) parts = spec.split("#");
  else parts = spec.split(",");
  const raw = {};
  for (let part of parts) {
    const p = part.split("=");
    raw[p[0]] = p.slice(1).join("=");
  }
  const turn = raw.turn === "end" ? "end" : "start";
  const saveAbilityString = raw.saveAbility ?? "";
  const saveAbility = (saveAbilityString.includes("|") ? saveAbilityString.split("|") : [saveAbilityString]).map((s) => s.trim().toLowerCase());
  const rollTypeString = raw.rollType ?? (saveAbility[0] ? "save" : "damage");
  const rollType = (rollTypeString.includes("|") ? rollTypeString.split("|") : [rollTypeString]).map((s) => s.trim().toLowerCase())[0];
  let saveDC = -1;
  if (raw.saveDC) {
    try {
      const dcValue = replaceAtFields(raw.saveDC, rollData, { blankValue: 0, maxIterations: 3 });
      saveDC = dcValue ? Roll.safeEval(dcValue) : -1;
    } catch (err) {
      TroubleShooter.recordError(err, `overTime effect | error evaluating saveDC ${raw.saveDC}`);
    }
  }
  let actionSave = void 0;
  if (raw.actionSave) {
    if (["dialog", "roll"].includes(raw.actionSave)) {
      actionSave = raw.actionSave;
    } else {
      console.warn(`midi-qol | parseOvertimeDetails | invalid actionSave: ${raw.actionSave}`);
      if (!["0", "false"].includes(raw.actionSave)) {
        actionSave = "roll";
      }
    }
  }
  let saveCount;
  let failCount;
  if (raw.saveCount !== void 0) {
    saveCount = parseCountAction(raw.saveCount, "remove");
  } else if (raw.saveRemove !== void 0) {
    foundry.utils.logCompatibilityWarning(
      `midi-qol | parseOvertimeDetails | saveRemove is deprecated, use saveCount instead (saveRemove=${raw.saveRemove})`,
      { since: "midi-qol 13.0.43", until: "midi-qol 13.1.0" }
    );
    if (raw.saveRemove === "true" || raw.saveRemove === "1") {
      saveCount = { count: 1, mode: "remove", consecutive: false };
    } else if (raw.saveRemove === "false" || raw.saveRemove === "0") {
      saveCount = { count: 0, mode: "permanent", consecutive: false };
    } else {
      saveCount = parseCountAction(raw.saveRemove, "remove");
    }
  }
  if (raw.failCount !== void 0) {
    failCount = parseCountAction(raw.failCount, "permanent");
  } else if (raw.failRemove !== void 0) {
    foundry.utils.logCompatibilityWarning(
      `midi-qol | parseOvertimeDetails | failRemove is deprecated, use failCount instead (failRemove=${raw.failRemove})`,
      { since: "midi-qol 13.0.43", until: "midi-qol 13.1.0" }
    );
    failCount = parseCountAction(raw.failRemove, "permanent");
  }
  if (saveCount === void 0 && failCount === void 0 && saveDC > -1) {
    saveCount = { count: 1, mode: "remove", consecutive: false };
  }
  return {
    turn,
    label: (raw.name ?? raw.label ?? effectName).replace(/"/g, ""),
    rollType,
    rollTypeString,
    saveAbility,
    saveAbilityString,
    saveDC,
    saveMagic: parseBool(raw.saveMagic, false),
    damageRoll: raw.damageRoll,
    damageType: raw.damageType ?? "",
    damageBeforeSave: parseBool(raw.damageBeforeSave, false),
    saveDamage: raw.saveDamage ?? "nodamage",
    saveCount,
    failCount,
    actionSave,
    applyCondition: raw.applyCondition ?? raw.condition,
    removeCondition: raw.removeCondition,
    allowIncapacitated: parseBool(raw.allowIncapacitated, false),
    killAnim: parseBool(raw.killAnim, false),
    macro: raw.macro,
    itemName: raw.itemName,
    chatFlavor: raw.chatFlavor ?? "",
    rollMode: raw.rollMode,
    fastForwardDamage: parseBool(raw.fastForwardDamage, true),
    fastForwardAttack: parseBool(raw.fastForwardAttack, true),
    autoRollAttack: parseBool(raw.autoRollAttack, true),
    autoRollDamage: parseBool(raw.autoRollDamage, true)
  };
}
__name(parseOvertimeDetails, "parseOvertimeDetails");
function serializeOvertimeDetails(data) {
  const parts = [];
  if (data.turn && data.turn !== "start") parts.push(`turn=${data.turn}`);
  else parts.push(`turn=start`);
  if (data.label) parts.push(`label=${data.label}`);
  if (data.rollType && data.rollType !== "save") parts.push(`rollType=${data.rollType}`);
  if (data.saveAbility) parts.push(`saveAbility=${data.saveAbility}`);
  if (data.saveDC !== void 0 && data.saveDC !== "" && data.saveDC !== "-1") parts.push(`saveDC=${data.saveDC}`);
  if (data.saveMagic) parts.push(`saveMagic=true`);
  if (data.damageRoll) parts.push(`damageRoll=${data.damageRoll}`);
  if (data.damageType) parts.push(`damageType=${data.damageType}`);
  if (data.damageBeforeSave) parts.push(`damageBeforeSave=true`);
  if (data.saveDamage && data.saveDamage !== "nodamage") parts.push(`saveDamage=${data.saveDamage}`);
  if (data.saveCountEnabled && data.saveCountNum) {
    const countStr = `${data.saveCountNum}${data.saveCountConsecutive ? "c" : ""}${data.saveCountMode === "remove" ? "-" : "+"}${data.saveCountEffectSpec || ""}`;
    parts.push(`saveCount=${countStr}`);
  }
  if (data.failCountEnabled && data.failCountNum) {
    const countStr = `${data.failCountNum}${data.failCountConsecutive ? "c" : ""}${data.failCountMode === "remove" ? "-" : "+"}${data.failCountEffectSpec || ""}`;
    parts.push(`failCount=${countStr}`);
  }
  if (data.actionSave && data.actionSave !== "none") parts.push(`actionSave=${data.actionSave}`);
  if (data.applyCondition) parts.push(`applyCondition=${data.applyCondition}`);
  if (data.removeCondition) parts.push(`removeCondition=${data.removeCondition}`);
  if (data.macro) parts.push(`macro=${data.macro}`);
  if (data.itemName) parts.push(`itemName=${data.itemName}`);
  if (data.chatFlavor) parts.push(`chatFlavor=${data.chatFlavor}`);
  if (data.rollMode && data.rollMode !== "default") parts.push(`rollMode=${data.rollMode}`);
  if (!data.fastForwardDamage) parts.push(`fastForwardDamage=false`);
  if (!data.fastForwardAttack) parts.push(`fastForwardAttack=false`);
  if (!data.autoRollAttack) parts.push(`autoRollAttack=false`);
  if (!data.autoRollDamage) parts.push(`autoRollDamage=false`);
  if (data.allowIncapacitated) parts.push(`allowIncapacitated=true`);
  if (data.killAnim) parts.push(`killAnim=true`);
  return parts.join(", ");
}
__name(serializeOvertimeDetails, "serializeOvertimeDetails");
function buildRollActivityData(details) {
  const { rollType, saveAbility, saveDC, saveMagic } = details;
  const activityData = {};
  if (rollType === "save" && saveAbility[0] && saveDC > -1) {
    activityData.type = "save";
    activityData.save = {
      ability: saveAbility,
      dc: { calculation: "", formula: String(saveDC) }
    };
  } else if (rollType === "check" && saveAbility[0] && saveDC > -1) {
    activityData.type = "check";
    activityData.check = {
      ability: saveAbility[0],
      dc: { calculation: "", formula: String(saveDC) }
    };
  } else if (rollType === "skill" && saveAbility[0] && saveDC > -1) {
    activityData.type = "check";
    activityData.check = {
      associated: saveAbility,
      dc: { calculation: "", formula: String(saveDC) }
    };
  }
  return activityData;
}
__name(buildRollActivityData, "buildRollActivityData");
async function gmOverTimeEffect(actor, effect, startTurn = true, options = { saveToUse: void 0, rollFlags: void 0, rollMode: void 0 }) {
  const endTurn = !startTurn;
  if (effect.disabled || effect.isSuppressed) return;
  const auraFlags = effect.flags?.ActiveAuras ?? {};
  if (auraFlags.isAura && auraFlags.ignoreSelf) return;
  const rollData = createConditionData({ actor, workflow: void 0, target: actor, effect });
  if (!rollData.flags) rollData.flags = actor.flags;
  rollData.flags.midiqol = rollData.flags[MODULE_ID];
  const changes = effect.changes.filter((change) => change.key.startsWith(`flags.${MODULE_ID}.OverTime`));
  if (changes.length > 0) for (let change of changes) {
    const details = parseOvertimeDetails(change.value, rollData, effect.name);
    if (!details) continue;
    if (details.applyCondition) {
      let value = replaceAtFields(details.applyCondition, rollData, { blankValue: 0, maxIterations: 3 });
      let result;
      try {
        result = await evalCondition(value, rollData, { async: true });
      } catch (err) {
        const message = `midi-qol | gmOverTimeEffect | error when evaluating overtime apply condition ${value} - assuming true`;
        TroubleShooter.recordError(err, message);
        console.warn(message, err);
        result = true;
      }
      if (!result) continue;
    }
    const { turn, label, rollTypeString, saveAbility, saveAbilityString, saveDC, rollMode, actionSave } = details;
    const changeTurnStart = turn === "start";
    const changeTurnEnd = turn === "end";
    const messageFlavor = {
      "save": `${GameSystemConfig.abilities[saveAbilityString]?.label ?? saveAbilityString} ${i18n("midi-qol.saving-throw")}`,
      "check": `${GameSystemConfig.abilities[saveAbilityString]?.label ?? saveAbilityString} ${i18n("midi-qol.ability-check")}`,
      "skill": `${GameSystemConfig.skills[saveAbilityString]?.label ?? saveAbilityString} ${i18n("midi-qol.skill-check")}`
    };
    const rollTypeArray = (rollTypeString.includes("|") ? rollTypeString.split("|") : [rollTypeString]).map((s) => s.trim().toLowerCase());
    if (endTurn) {
      const chatCardUuids = effect.getFlag(MODULE_ID, "overtimeChatCardUuids");
      if (chatCardUuids) for (let chatCardUuid of chatCardUuids) {
        const chatCard = fromUuidSync(chatCardUuid);
        chatCard?.delete();
      }
    }
    if (options.isActionSave && actionSave === "dialog") {
      continue;
    }
    let owner = playerForActor(actor) ?? preferredActiveGM();
    if (!owner?.active) owner = preferredActiveGM();
    const actionSaveCtx = { actor, effect, details, owner, messageFlavor };
    if (actionSave && startTurn && actionSave === "dialog") {
      const shouldContinue = await handleActionSaveDialog(actionSaveCtx);
      if (!shouldContinue) return effect.id;
    } else if (actionSave && actionSave === "roll" && options.isActionSave && options.saveToUse) {
      const result = await handleActionSaveRollResponse(actionSaveCtx, options, rollTypeArray);
      if (result !== void 0) {
        if (result === null) continue;
        return result;
      }
    } else if (actionSave === "roll" && startTurn) {
      const result = await handleActionSaveRollRequest(actionSaveCtx, changeTurnEnd);
      if (result !== void 0) return result;
    }
    let actionSaveSuccess = effect.flags?.[MODULE_ID]?.actionSaveSuccess;
    if (actionSaveSuccess === true && changeTurnEnd) {
      await expireEffects(actor, [effect], { "expiry-reason": "midi-qol:overTime:actionSave" });
      return effect.id;
    }
    if (endTurn && changeTurnEnd || startTurn && changeTurnStart) {
      const { damageRoll, damageType, damageBeforeSave, macro: macroToCall, allowIncapacitated, killAnim, saveCount, failCount, fastForwardAttack, fastForwardDamage, autoRollAttack, autoRollDamage } = details;
      if (debugEnabled > 0) warn(`gmOverTimeEffect | Overtime provided data is `, details);
      if (debugEnabled > 0) warn(`gmOverTimeEffect | OverTime label=${label} startTurn=${startTurn} endTurn=${endTurn} damageBeforeSave=${damageBeforeSave} saveDC=${saveDC} saveAbility=${saveAbility} damageRoll=${damageRoll} damageType=${damageType}`);
      const effectMarkedPermanent = !!effect.flags?.[MODULE_ID]?.overtime?.permanent;
      const isPermanent = !!actionSave || effectMarkedPermanent;
      const { itemData, activityData } = await buildOvertimeItemData({ actor, effect, details, label, isPermanent });
      const theTargetToken = getOrCreateTokenForActor(actor);
      if (damageRoll && activityData.damage?.parts?.[0]) {
        let damageRollString = damageRoll;
        let stackCount = effect.flags.dae?.stacks ?? 1;
        if (globalThis.EffectCounter && theTargetToken) {
          const counter = globalThis.EffectCounter.findCounter(getTokenDocument(theTargetToken), effect.img ?? effect.icon);
          if (counter) stackCount = counter.getValue();
        }
        for (let i = 1; i < stackCount; i++)
          damageRollString = `${damageRollString} + ${damageRoll}`;
        activityData.damage.parts[0].custom.formula = damageRollString;
      }
      foundry.utils.setProperty(itemData.flags, "midi-qol.forceCEOff", true);
      if (killAnim) foundry.utils.setProperty(itemData.flags, "autoanimations.killAnim", true);
      if (macroToCall) {
        foundry.utils.setProperty(itemData, `flags.${MODULE_ID}.onUseMacroName`, macroToCall);
        foundry.utils.setProperty(itemData, `flags.${MODULE_ID}.onUseMacroParts`, new OnUseMacros(macroToCall));
      }
      let origin = fromUuidSync(effect.origin);
      while (origin && !(origin instanceof Actor)) {
        origin = origin?.parent;
      }
      itemData.system.activities = { "overtime": activityData };
      foundry.utils.setProperty(itemData, `flags.${MODULE_ID}.syntheticItem`, true);
      let ownedItem = new CONFIG.Item.documentClass(itemData, { parent: origin instanceof Actor ? origin : actor });
      ownedItem.prepareData();
      ownedItem.prepareFinalAttributes();
      if (!actionSave && !isPermanent && (saveCount || failCount) && saveDC > -1) {
        const activityUuid = ownedItem.system.activities.contents[0].uuid;
        savedOverTimeEffectsToDelete[activityUuid] = {
          uuid: effect.uuid,
          saveCount: saveCount?.count,
          saveCountAction: saveCount ? countActionToString(saveCount) : void 0,
          saveConsecutive: saveCount?.consecutive ?? false,
          failCount: failCount?.count,
          failCountAction: failCount ? countActionToString(failCount) : void 0,
          failConsecutive: failCount?.consecutive ?? false
        };
      }
      if (details.removeCondition) {
        let value = replaceAtFields(details.removeCondition, rollData, { blankValue: 0, maxIterations: 3 });
        let remove;
        try {
          remove = await evalCondition(value, rollData, { errorReturn: true, async: true });
        } catch (err) {
          const message = `midi-qol | gmOverTimeEffect | error when evaluating overtime remove condition ${value} - assuming true`;
          TroubleShooter.recordError(err, message);
          console.warn(message, err);
          remove = true;
        }
        if (remove) {
          const otKey = ownedItem.system.activities.contents[0].uuid;
          overTimeEffectsToDelete[otKey] = { uuid: effect.uuid };
        }
      }
      try {
        const options2 = {
          createWorkflow: true,
          configureDialog: false,
          saveDC,
          fastForwardAttack,
          fastForwardDamage,
          autoRollAttack,
          autoRollDamage: autoRollDamage ? "onHit" : "none",
          checkGMStatus: true,
          targetsToUse: /* @__PURE__ */ new Set([theTargetToken]),
          // targetUuids: [theTargetUuid],
          ignoreUserTargets: true,
          workflowOptions: { targetConfirmation: "none", isOverTime: true, allowIncapacitated },
          flags: {
            dnd5e: { "itemData": ownedItem.toObject() }
          }
        };
        foundry.utils.setProperty(options2, `flags.${MODULE_ID}.isOverTime`, true);
        await completeItemUse(ownedItem, { midiOptions: options2 }, { configure: false }, { rollMode, systemCard: false });
        if (actionSaveSuccess) {
          await expireEffects(actor, [effect], { "expiry-reason": "midi-qol:overTime:actionSave" });
        }
        return effect.id;
      } catch (err) {
        const message = "midi-qol | completeItemUse | error";
        TroubleShooter.recordError(err, message);
        console.warn(message, err);
      } finally {
      }
    }
  }
}
__name(gmOverTimeEffect, "gmOverTimeEffect");
async function _processActivityOverTime(combat, data, options, userId) {
  if (!preferredActiveGM()?.isSelf) return;
  const combatStart = combat.round === 1 && combat.turn === 0;
  let prev = (combat.previous?.round ?? 0) * 100 + (combat.previous?.turn ?? 0);
  let testTurn = combat.previous?.turn ?? 0;
  let testRound = combat.previous?.round ?? 0;
  const last = (data.round ?? combat.current.round ?? 0) * 100 + (data.turn ?? combat.current.turn ?? 0);
  let toTest = prev;
  let count = 0;
  const maxIterations = combatStart ? 1 : 200;
  while (toTest <= last && count < maxIterations) {
    count += 1;
    if (!combat.id || !game.combats?.get(combat.id)) return;
    const actor = combat.turns[testTurn]?.actor;
    const endTurn = toTest < last;
    const startTurn = toTest > prev || combatStart;
    if (actor) for (let effect of actor.appliedEffects) {
      if (effect.changes.some((change) => change.key.startsWith(`flags.${MODULE_ID}.ActivityOverTime`))) {
        await doActivityOverTimeEffect(actor, effect, startTurn);
      }
    }
    testTurn += 1;
    if (testTurn === combat.turns.length) {
      testTurn = 0;
      testRound += 1;
      toTest = testRound * 100;
    } else toTest += 1;
  }
}
__name(_processActivityOverTime, "_processActivityOverTime");
async function _processOverTime(combat, data, options, userId) {
  if (!preferredActiveGM()?.isSelf) return;
  const combatStart = combat.round === 1 && combat.turn === 0;
  let prev = (combat.previous?.round ?? 0) * 100 + (combat.previous?.turn ?? 0);
  let testTurn = combat.previous?.turn ?? 0;
  let testRound = combat.previous?.round ?? 0;
  const last = (data.round ?? combat.current.round ?? 0) * 100 + (data.turn ?? combat.current.turn ?? 0);
  let toTest = prev;
  let count = 0;
  const maxIterations = combatStart ? 1 : 200;
  while (toTest <= last && count < maxIterations) {
    count += 1;
    if (!combat.id || !game.combats?.get(combat.id)) return;
    const actor = combat.turns[testTurn]?.actor;
    const endTurn = toTest < last;
    const startTurn = toTest > prev || combatStart;
    if (actor && toTest !== prev && !installedModules.get("times-up")) {
      await unTimedExecuteAsGM("removeActionBonusReaction", { actorUuid: actor.uuid });
    }
    if (actor && toTest !== prev) {
      removeActionUsed(actor);
    }
    if (actor) for (let effect of actor.appliedEffects) {
      if (effect.changes.some((change) => change.key.startsWith(`flags.${MODULE_ID}.OverTime`))) {
        await doOverTimeEffect(actor, effect, startTurn);
      }
    }
    testTurn += 1;
    if (testTurn === combat.turns.length) {
      testTurn = 0;
      testRound += 1;
      toTest = testRound * 100;
    } else toTest += 1;
  }
}
__name(_processOverTime, "_processOverTime");
async function completeActivityUse2(activityRef, usage = {}, dialog = {}, message = {}) {
  activityRef = foundry.utils.deepClone(activityRef);
  usage.midiOptions ??= {};
  let targetsToUse = /* @__PURE__ */ new Set();
  if (usage.midiOptions.targetsToUse && !(usage.midiOptions.targetsToUse instanceof Set)) {
    error2("completeActivityUse | targetsToUse is not a Set");
  } else if (usage.midiOptions.targetsToUse) {
    targetsToUse = usage.midiOptions.targetsToUse.map((t) => getToken(t)).filter((t) => !!t);
  }
  if (usage.midiOptions.targetUuids) {
    for (let targetUuid of usage.midiOptions.targetUuids) {
      const theTarget = getToken(targetUuid);
      if (theTarget) {
        targetsToUse.add(theTarget);
      }
    }
  }
  const activity = typeof activityRef === "string" ? fromUuidSync(activityRef) : activityRef;
  if (!activity) return;
  let asUser = typeof usage.midiOptions.asUser === "string" ? game.users?.get(usage.midiOptions.asUser) : usage.midiOptions.asUser;
  const asUserActive = asUser?.active;
  if (!asUserActive && (usage.midiOptions.checkGMstatus && !game.user?.isGM))
    asUser = preferredActiveGM();
  if (!asUser) asUser = game.user;
  foundry.utils.setProperty(usage, "midiOptions.workflowOptions.forceCompletion", true);
  let localRoll = asUser?.id === game.user?.id;
  if (localRoll) {
    return await new Promise((resolve) => {
      const maxWait = 90;
      const localSavedTargetIds = Array.from(game.user?.targets ?? []).map((t) => t.id);
      if (usage.midiOptions.ignoreUserTargets) updateUserTargets([]);
      if (game.user && targetsToUse.size === 0 && activity.target?.affects?.type === "self") {
        const selfTarget = getToken(activity.item.actor);
        if (selfTarget) {
          updateUserTargets([selfTarget.id]);
          targetsToUse = /* @__PURE__ */ new Set([selfTarget]);
        } else {
          updateUserTargets([]);
        }
      } else if (targetsToUse.size === 0 && !usage.midiOptions.ignoreUserTargets) {
        targetsToUse = new Set(game.user?.targets);
      }
      let abortHookName = `midi-qol.preAbort.${activity?.uuid}`;
      if (!activity) {
        abortHookName = "midi-qol.preAbort";
      }
      usage.sequenceId = foundry.utils.randomID();
      let completeHookName = `midi-qol.postCleanup.${activity.uuid}`;
      if (!activity || activity.activity?.id || activity.type === "cast") {
        completeHookName = "midi-qol.postCleanup";
      }
      let timeoutId;
      const cleanupAndResolve = /* @__PURE__ */ __name((result, source) => {
        if (timeoutId !== void 0) {
          clearTimeout(timeoutId);
          timeoutId = void 0;
        }
        Hooks.off(abortHookName, abortHookId);
        Hooks.off(completeHookName, completeHookId);
        if (source && debugEnabled > 0) warn(`completeActivityUse ${source}: ${activity?.item?.name} ${completeHookName}`);
        updateUserTargets(localSavedTargetIds);
        resolve(result);
      }, "cleanupAndResolve");
      const abortHookId = Hooks.on(abortHookName, (workflow) => {
        if (workflow.sequenceId !== usage.sequenceId) return;
        cleanupAndResolve(workflow, "preAbort");
      });
      const completeHookId = Hooks.on(completeHookName, (workflow) => {
        if (workflow.sequenceId !== usage.sequenceId) return;
        cleanupAndResolve(workflow, "postCleanup");
      });
      timeoutId = setTimeout(() => {
        cleanupAndResolve(void 0, "timeout");
      }, maxWait * 1e3);
      usage.midiOptions.targetsToUse = targetsToUse;
      activity.use(usage, dialog, message).then((result) => {
        if (!result) {
          cleanupAndResolve(result);
        }
      }).catch((err) => {
        TroubleShooter.recordError(err, "completeActivityUse | activity.use failed");
        cleanupAndResolve(void 0, "error");
      });
    });
  } else {
    const newUsage = foundry.utils.deepClone(usage);
    newUsage.midiOptions ??= {};
    newUsage.midiOptions.targetsToUse = Array.from(targetsToUse).map((t) => t.document.uuid);
    if (usage.midiOptions.rollAs) newUsage.midiOptions.rollAs = usage.midiOptions.rollAs.uuid;
    const data = {
      activityUuid: activity.uuid,
      actorUuid: activity.item.parent?.uuid,
      usage: newUsage,
      dialog,
      message
    };
    if (asUser)
      return await socketlibSocket.executeAsUser("completeActivityUse", asUser.id, data);
    else
      return await timedAwaitExecuteAsGM("completeActivityUse", data);
  }
}
__name(completeActivityUse2, "completeActivityUse");
async function completeItemUse(itemRef, config = {}, dialog = {}, message = {}) {
  const item = typeof itemRef === "string" ? await fromUuid(itemRef) : itemRef;
  const { legacy, chooseActivity, ...activityConfig } = config;
  if (legacy) {
    error2("completeItemUse | legacy rolls are no longer supported");
    return;
  }
  config.midiOptions ??= {};
  if (!(item instanceof CONFIG.Item.documentClass)) {
    error2("completeItemUse only works for items", item);
    return;
  }
  if (config.midiOptions.activityId || config.midiOptions.activityIdentifier) {
    const selected = item.system.activities.find((a) => a.id === config.midiOptions.activityId || a.identifier === config.midiOptions.activityIdentifier);
    if (selected) return completeActivityUse2(selected, activityConfig, dialog, message);
  }
  const linkedCastActivity = item.type === "spell" && item.system.linkedActivity;
  if (linkedCastActivity) {
    return completeActivityUse2(linkedCastActivity, activityConfig, dialog, message);
  }
  const activities = item.system.activities?.filter(
    (a) => a.canUse !== false && !item.flags.dnd5e?.riders?.activity?.includes(a.id) && !a.midiProperties?.automationOnly
  );
  if (activities.length === 0) {
    return item.displayCard(message);
  }
  const activity = await MidiActivityChoiceDialog.create(item, { chooseActivity });
  if (activity) return completeActivityUse2(activity, activityConfig, dialog, message);
  return void 0;
}
__name(completeItemUse, "completeItemUse");
function untargetAllTokens(combat) {
  let prevTurn = (combat.current.turn ?? 0) - 1;
  if (prevTurn === -1)
    prevTurn = combat.turns.length - 1;
  const previous = combat.turns[prevTurn];
  if (game.user?.isGM && ["allGM", "all"].includes(autoRemoveTargets) || autoRemoveTargets === "all" && canvas.tokens?.controlled.find((t) => t.id === previous.token?.id)) {
    game.user?.targets.forEach((t) => {
      t.setTarget(false, { releaseOthers: false });
    });
  }
}
__name(untargetAllTokens, "untargetAllTokens");
function getVitalityItem(actor) {
  if (!actor) return void 0;
  const itemName = checkRule("vitalityItem");
  if (typeof itemName !== "string" || !itemName) return void 0;
  return actor.items.getName(itemName) ?? void 0;
}
__name(getVitalityItem, "getVitalityItem");
function getVitalityValue(actor) {
  if (!actor) return void 0;
  const vitalityItem = getVitalityItem(actor);
  if (vitalityItem) {
    return vitalityItem.system.uses?.value ?? 0;
  }
  return void 0;
}
__name(getVitalityValue, "getVitalityValue");
function getVitalityMax(actor) {
  if (!actor) return void 0;
  const vitalityItem = getVitalityItem(actor);
  if (vitalityItem) {
    return vitalityItem.system.uses?.max ?? 0;
  }
  return void 0;
}
__name(getVitalityMax, "getVitalityMax");
function hasVitalitySystem(actor) {
  if (!actor) return false;
  return !!getVitalityItem(actor);
}
__name(hasVitalitySystem, "hasVitalitySystem");
function checkDefeated(actorRef) {
  const actor = getActor(actorRef);
  if (!actor) return 0;
  return hasCondition(actor, CONFIG.specialStatusEffects.DEFEATED) || hasCondition(actor, configSettings.midiDeadCondition);
}
__name(checkDefeated, "checkDefeated");
function checkIncapacitated(actorRef, logResult = true, warning = false) {
  const actor = getActor(actorRef);
  if (!actor) return false;
  let status = false;
  if (actor.system.traits?.ci?.value?.has("incapacitated")) return false;
  const vitality = getVitalityValue(actor);
  if (vitality !== void 0) {
    const deathRequires = configSettings.optionalRules.vitalityDeathRequires || "vitalityOnly";
    const hpValue = actor?.system.attributes?.hp?.value ?? 0;
    if (deathRequires === "vitalityOnly") {
      if (vitality <= 0) {
        status = "dead";
      }
    } else {
      if (vitality <= 0 && hpValue <= 0) {
        status = "dead";
      }
    }
  } else {
    if (!actor.system?.attributes?.hp?.value) {
      debug("No hp attribute for ", actor);
    }
    if (actor?.system?.attributes?.hp?.value <= 0) {
      status = "dead";
    }
  }
  if (configSettings.midiUnconsciousCondition && hasCondition(actor, configSettings.midiUnconsciousCondition)) {
    status = configSettings.midiUnconsciousCondition;
  }
  if (configSettings.midiDeadCondition && hasCondition(actor, configSettings.midiDeadCondition)) {
    status = configSettings.midiDeadCondition;
  }
  const incapCondition = (globalThis.MidiQOL.incapacitatedConditions ?? ["incapacitated"]).find((cond) => hasCondition(actor, cond));
  if (incapCondition) {
    status = incapCondition;
  }
  if (status) logIncapacitatedCheckResult(actor.name ?? "unknown", status, logResult, warning);
  return status;
}
__name(checkIncapacitated, "checkIncapacitated");
function logIncapacitatedCheckResult(actorName, status, logResult = true, warning = false) {
  const displayString = status === "incapacitated" ? `${actorName} is ${getStatusName(status)}` : `${actorName} is ${getStatusName(status)} and therefore ${getStatusName("incapacitated")}`;
  if (logResult) log(displayString);
  if (warning) ui.notifications?.warn(displayString);
}
__name(logIncapacitatedCheckResult, "logIncapacitatedCheckResult");
function getUnitDist(x1, y1, z1, token2) {
  if (!canvas.dimensions) return 0;
  const unitsToPixel = canvas.dimensions.size / canvas.dimensions.distance;
  z1 = z1 * unitsToPixel;
  const x2 = token2.center.x;
  const y2 = token2.center.y;
  const z2 = token2.document.elevation * unitsToPixel;
  const d = Math.sqrt(
    Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2)
  ) / unitsToPixel;
  return d;
}
__name(getUnitDist, "getUnitDist");
function checkDistance(t1, t2, distance, options = { wallsBlock: false, includeCover: true }) {
  const { wallsBlock = false, includeCover = true } = options;
  const dist = getDistance(t1, t2, { wallsBlock, includeCover });
  return 0 <= dist && dist <= distance;
}
__name(checkDistance, "checkDistance");
function getTokenPerimeterPolygon(token) {
  const shape = token.getShape();
  const { x, y } = token.document;
  let polygon;
  if (shape instanceof PIXI.Polygon) {
    polygon = shape;
  } else if (shape instanceof PIXI.Circle) {
    polygon = shape.toPolygon();
  } else if (shape instanceof PIXI.Ellipse) {
    polygon = ellipseToPolygon(shape);
  } else {
    polygon = shape.toPolygon();
  }
  const pts = polygon.points;
  const worldPts = new Array(pts.length);
  for (let i = 0; i < pts.length; i += 2) {
    worldPts[i] = pts[i] + x;
    worldPts[i + 1] = pts[i + 1] + y;
  }
  return new PIXI.Polygon(worldPts);
}
__name(getTokenPerimeterPolygon, "getTokenPerimeterPolygon");
function ellipseToPolygon(ellipse, segments = 24) {
  const pts = [];
  for (let i = 0; i < segments; i++) {
    const angle = 2 * Math.PI * i / segments;
    pts.push(ellipse.x + ellipse.width * Math.cos(angle));
    pts.push(ellipse.y + ellipse.height * Math.sin(angle));
  }
  return new PIXI.Polygon(pts);
}
__name(ellipseToPolygon, "ellipseToPolygon");
function closestPointOnPerimeter(polygon, px, py) {
  const pts = polygon.points;
  const numPts = pts.length;
  if (numPts < 4) return { x: px, y: py };
  const c = { x: px, y: py };
  let bestDistSq = Infinity;
  let best = { x: px, y: py };
  for (let i = 0; i < numPts; i += 2) {
    const ni = (i + 2) % numPts;
    const a = { x: pts[i], y: pts[i + 1] };
    const b = { x: pts[ni], y: pts[ni + 1] };
    if (a.x === b.x && a.y === b.y) continue;
    const closest = foundry.utils.closestPointToSegment(c, a, b);
    const dx = closest.x - px;
    const dy = closest.y - py;
    const distSq = dx * dx + dy * dy;
    if (distSq < bestDistSq) {
      bestDistSq = distSq;
      best = closest;
    }
  }
  return best;
}
__name(closestPointOnPerimeter, "closestPointOnPerimeter");
function closestPerimeterPoints(poly1, poly2) {
  const b1 = poly1.getBounds();
  const b2 = poly2.getBounds();
  const overlapX = b1.left < b2.right && b1.right > b2.left;
  const overlapY = b1.top < b2.bottom && b1.bottom > b2.top;
  if (overlapX && overlapY) {
    const c1 = polygonCentroid(poly1);
    const c22 = polygonCentroid(poly2);
    if (poly1.contains(c22.x, c22.y) || poly2.contains(c1.x, c1.y)) {
      const midX = (c1.x + c22.x) / 2;
      const midY = (c1.y + c22.y) / 2;
      return { p1: { x: midX, y: midY }, p2: { x: midX, y: midY } };
    }
  }
  const c2 = polygonCentroid(poly2);
  let p1 = closestPointOnPerimeter(poly1, c2.x, c2.y);
  let p2 = closestPointOnPerimeter(poly2, p1.x, p1.y);
  p1 = closestPointOnPerimeter(poly1, p2.x, p2.y);
  p2 = closestPointOnPerimeter(poly2, p1.x, p1.y);
  return { p1, p2 };
}
__name(closestPerimeterPoints, "closestPerimeterPoints");
function polygonCentroid(poly) {
  const pts = poly.points;
  let cx = 0, cy = 0;
  const count = pts.length / 2;
  for (let i = 0; i < pts.length; i += 2) {
    cx += pts[i];
    cy += pts[i + 1];
  }
  return { x: cx / count, y: cy / count };
}
__name(polygonCentroid, "polygonCentroid");
function checkWallCollision(origin, dest, options) {
  const { wallsBlockRange, coverVisible, source } = options;
  let blocked;
  switch (wallsBlockRange) {
    case "center": {
      blocked = !!CONFIG.Canvas.polygonBackends.sight.testCollision(origin, dest, {
        source,
        mode: "any",
        type: "sight"
      });
      return blocked;
    }
    case "centerLevels":
    case "levelsautocover":
      if (installedModules.get("levels")) {
        if (coverVisible === false) {
          return true;
        }
        if (coverVisible === void 0) {
          const p1 = { x: origin.x, y: origin.y, z: options.originElevation ?? 0 };
          const p2 = { x: dest.x, y: dest.y, z: options.destElevation ?? 0 };
          const baseToBase = CONFIG.Levels.API.testCollision(p1, p2, "collision");
          const p1Top = { x: origin.x, y: origin.y, z: options.originTopElevation ?? p1.z };
          const p2Top = { x: dest.x, y: dest.y, z: options.destTopElevation ?? p2.z };
          const topToBase = CONFIG.Levels.API.testCollision(p1Top, p2Top, "collision");
          blocked = baseToBase && topToBase;
          return blocked;
        }
        return false;
      }
      blocked = !!CONFIG.Canvas.polygonBackends.sight.testCollision(origin, dest, {
        source,
        mode: "any",
        type: "sight"
      });
      return blocked;
    case "alternative":
    case "simbuls-cover-calculator":
      if (coverVisible === void 0) {
        blocked = !!CONFIG.Canvas.polygonBackends.sight.testCollision(origin, dest, {
          source,
          mode: "any",
          type: "sight"
        });
        return blocked;
      }
      return false;
    case "simplecover5e":
      return false;
    case "tokencover":
      return false;
    case "none":
    default:
      return false;
  }
}
__name(checkWallCollision, "checkWallCollision");
function shapesOverlap(poly1, poly2) {
  const b1 = poly1.getBounds();
  const b2 = poly2.getBounds();
  if (b1.right < b2.left || b1.left > b2.right || b1.bottom < b2.top || b1.top > b2.bottom) return false;
  const c1 = polygonCentroid(poly1);
  const c2 = polygonCentroid(poly2);
  return poly1.contains(c2.x, c2.y) || poly2.contains(c1.x, c1.y);
}
__name(shapesOverlap, "shapesOverlap");
function computeWallBlockPreCheck(t1, t2, wallsBlock, includeCover) {
  const wallsBlockRange = configSettings.optionalRules.wallsBlockRange;
  if (installedModules.get("levelsautocover") && game.settings.get("levelsautocover", "apiMode") && wallsBlock && wallsBlockRange === "levelsautocover" && includeCover) {
    const levelsautocoverData = AutoCover.calculateCover(t1, t2, getLevelsAutoCoverOptions());
    const coverVisible = levelsautocoverData.rawCover > 0;
    const result = coverVisible ? { blocked: false, coverVisible: true } : { blocked: true };
    return result;
  } else if (globalThis.CoverCalculator && wallsBlockRange === "simbuls-cover-calculator" && includeCover) {
    if (t1 === t2) return { blocked: false, coverVisible: void 0 };
    const coverData = globalThis.CoverCalculator.Cover(t1, t2);
    if (debugEnabled > 0) warn("wallBlockPreCheck | simbuls cover calculator ", t1.name, t2.name, coverData);
    const result = coverData?.data.results.cover === 3 && wallsBlock ? { blocked: true } : { blocked: false, coverVisible: true };
    return result;
  } else if (installedModules.get("tokencover") && wallsBlockRange === "tokencover" && includeCover) {
    const coverValue = calcTokenCover(t1, t2);
    const result = coverValue === 3 && wallsBlock ? { blocked: true } : { blocked: false, coverVisible: true };
    return result;
  } else if (installedModules.get("simplecover5e") && wallsBlockRange === "simplecover5e" && wallsBlock) {
    const simpleCoverApi = game.modules.get("simplecover5e")?.api;
    if (simpleCoverApi) {
      const coverResult = simpleCoverApi.getCover({
        attacker: t1,
        target: t2,
        scene: t1.scene,
        losCheck: true
      });
      const result = coverResult?.bonus === null || coverResult?.cover === "total" ? { blocked: true } : { blocked: false, coverVisible: true };
      return result;
    }
  }
  return { blocked: false, coverVisible: void 0 };
}
__name(computeWallBlockPreCheck, "computeWallBlockPreCheck");
function computePerimeterDistance(t1, t2, options = {}) {
  let { wallsBlock = false, includeElevation = true, includeCover = true } = options;
  if (!canvas?.scene || !canvas.grid || !canvas.dimensions) return -1;
  const token1 = getPlaceable(t1);
  const token2 = getPlaceable(t2);
  if (!token1 || !token2) return -1;
  if (token1 === token2) return 0;
  const actor = token1.actor;
  const ignoreWallsFlag = actor ? foundry.utils.getProperty(actor, `flags.${MODULE_ID}.ignoreWalls`) : false;
  if (ignoreWallsFlag) {
    wallsBlock = false;
  }
  const poly1 = getTokenPerimeterPolygon(token1);
  const poly2 = getTokenPerimeterPolygon(token2);
  const gridDistance = canvas.dimensions.distance;
  if (shapesOverlap(poly1, poly2)) {
    return 0;
  }
  const { p1, p2 } = closestPerimeterPoints(poly1, poly2);
  const origin = new PIXI.Point(p1.x, p1.y);
  const dest = new PIXI.Point(p2.x, p2.y);
  if (wallsBlock) {
    const wallsBlockRange = configSettings.optionalRules.wallsBlockRange;
    if (wallsBlockRange !== "none") {
      const preCheck = computeWallBlockPreCheck(token1, token2, wallsBlock, includeCover);
      if (preCheck.blocked) return -1;
      if (preCheck.coverVisible === void 0) {
        const srcCenter = token1.center;
        const tgtCenter = token2.center;
        const t = Math.min(token2.w, token2.h) / 4;
        const t2half = Math.min(token2.w, token2.h) / 2 - 2;
        const offsets = t > 0 ? [
          [0, 0],
          [-t, -t],
          [-t, t],
          [t, t],
          [t, -t],
          [-t, 0],
          [t, 0],
          [0, -t],
          [0, t],
          [-t2half, -t2half],
          [-t2half, t2half],
          [t2half, t2half],
          [t2half, -t2half],
          [-t2half, 0],
          [t2half, 0],
          [0, -t2half],
          [0, t2half]
        ] : [[0, 0]];
        const testPoints = offsets.map((o) => new PIXI.Point(tgtCenter.x + o[0], tgtCenter.y + o[1]));
        const srcOrigin = new PIXI.Point(srcCenter.x, srcCenter.y);
        const t1Elevation = token1.document.elevation ?? 0;
        const t2Elevation = token2.document.elevation ?? 0;
        const t1DocSize = Math.max(token1.document.height ?? 1, token1.document.width ?? 1);
        const t2DocSize = Math.min(token2.document.height ?? 1, token2.document.width ?? 1);
        const t1TopElevation = t1Elevation + t1DocSize * (canvas.dimensions?.distance ?? 5);
        const t2TopElevation = t2Elevation + t2DocSize * (canvas.dimensions?.distance ?? 5);
        let allBlocked = true;
        for (const tgtPt of testPoints) {
          const collision = checkWallCollision(srcOrigin, tgtPt, {
            source: token1.document,
            wallsBlockRange,
            coverVisible: preCheck.coverVisible,
            originElevation: t1Elevation,
            destElevation: t2Elevation,
            originTopElevation: t1TopElevation,
            destTopElevation: t2TopElevation
          });
          if (!collision) {
            allBlocked = false;
            break;
          }
        }
        if (allBlocked) {
          return -1;
        }
      }
    }
  }
  const isGridless = canvas.grid.isGridless;
  const useGriddedGridless = isGridless && configSettings.griddedGridless;
  let distance;
  if (isGridless && !useGriddedGridless) {
    const dx = dest.x - origin.x;
    const dy = dest.y - origin.y;
    const pixelDistance = Math.sqrt(dx * dx + dy * dy);
    const gridSize = canvas.dimensions?.size ?? 100;
    distance = pixelDistance / gridSize * gridDistance;
  } else if (useGriddedGridless) {
    const canvasGridProxy = createSquareGridProxy();
    const result = canvasGridProxy.measurePath([origin, dest], {});
    distance = result?.distance ?? 0;
  } else {
    const result = canvas.grid.measurePath([origin, dest], {});
    distance = result?.distance ?? 0;
  }
  if (includeElevation) {
    const heightDifference = computeTokenHeightDifference(token1, token2);
    distance = combineDistanceWithHeight(distance, heightDifference);
  }
  return distance;
}
__name(computePerimeterDistance, "computePerimeterDistance");
function measurePointDistance(p1, p2) {
  if (!canvas?.grid) return 0;
  const isGridless = canvas.grid.isGridless;
  if (isGridless && configSettings.griddedGridless) {
    const proxy = createSquareGridProxy();
    return proxy.measurePath([p1, p2], {})?.distance ?? 0;
  }
  return canvas.grid.measurePath([p1, p2], {})?.distance ?? 0;
}
__name(measurePointDistance, "measurePointDistance");
var SCENE_UNIT_TO_DND5E = {
  ft: "ft",
  feet: "ft",
  foot: "ft",
  m: "m",
  meter: "m",
  meters: "m",
  metre: "m",
  metres: "m"
};
function rangeToSceneUnits(value, units) {
  if (!value || !units) return value ?? 0;
  const sceneUnit = canvas?.scene?.grid?.units?.toLowerCase() ?? "";
  const sceneUnitDnd = SCENE_UNIT_TO_DND5E[sceneUnit] ?? sceneUnit;
  return globalThis.dnd5e?.utils?.convertLength?.(value, units, sceneUnitDnd, { strict: false }) ?? value;
}
__name(rangeToSceneUnits, "rangeToSceneUnits");
function getDistance(t1, t2, options = {}) {
  const { wallsBlock = false, includeCover = true, includeElevation = configSettings.optionalRules.distanceIncludesHeight } = options;
  const measurementMode = configSettings.optionalRules.distanceMeasurement ?? "center";
  const wallsBlockRange = configSettings.optionalRules.wallsBlockRange;
  const gridDistance = canvas?.dimensions?.distance ?? 5;
  const fudgeFactor = configSettings.gridlessFudge ?? 0;
  let distance = 0;
  if (measurementMode === "default") {
    const token1 = getPlaceable(t1);
    const token2 = getPlaceable(t2);
    if (!token1 || !token2 || !canvas?.grid) return -1;
    if (token1 === token2) return 0;
    if (wallsBlock) {
      const preCheck = computeWallBlockPreCheck(token1, token2, wallsBlock, includeCover);
      if (preCheck.blocked) return -1;
      if (preCheck.coverVisible === void 0) {
        const origin = new PIXI.Point(token1.center.x, token1.center.y);
        const dest = new PIXI.Point(token2.center.x, token2.center.y);
        const collision = checkWallCollision(origin, dest, {
          source: token1.document,
          wallsBlockRange: configSettings.optionalRules.wallsBlockRange,
          coverVisible: void 0,
          originElevation: token1.document.elevation ?? 0,
          destElevation: token2.document.elevation ?? 0
        });
        if (collision) return -1;
      }
    }
    const result = canvas.grid.measurePath([token1.center, token2.center], {});
    distance = result?.distance ?? 0;
  } else if (measurementMode === "center") {
    distance = computeDistance(t1, t2, { wallsBlock, includeCover, includeElevation });
  } else if (measurementMode === "perimeter") {
    const rawPerimeterDistance = computePerimeterDistance(t1, t2, {
      wallsBlock,
      includeCover,
      includeElevation
    });
    if (rawPerimeterDistance < 0) {
      distance = -1;
    } else {
      distance = gridDistance + Math.max(0, rawPerimeterDistance - fudgeFactor);
    }
  }
  return distance;
}
__name(getDistance, "getDistance");
function computeTokenHeightDifference(token1, token2) {
  if (!canvas?.dimensions) return 0;
  const gridDistance = canvas.dimensions.distance ?? 5;
  const t1Elevation = token1.document.elevation ?? 0;
  const t2Elevation = token2.document.elevation ?? 0;
  const t1DocWidth = token1.document.width ?? 1;
  const t1DocHeight = token1.document.height ?? 1;
  const t2DocWidth = token2.document.width ?? 1;
  const t2DocHeight = token2.document.height ?? 1;
  const t1TopElevation = t1Elevation + Math.max(t1DocHeight, t1DocWidth) * gridDistance;
  const t2TopElevation = t2Elevation + Math.min(t2DocHeight, t2DocWidth) * gridDistance;
  if (t2Elevation > t1Elevation && t2Elevation < t1TopElevation || t1Elevation > t2Elevation && t1Elevation < t2TopElevation) {
    return 0;
  }
  if (t1Elevation < t2Elevation) {
    return Math.max(0, t2Elevation - t1TopElevation) + gridDistance;
  }
  if (t1Elevation > t2Elevation) {
    return Math.max(0, t1Elevation - t2TopElevation) + gridDistance;
  }
  return 0;
}
__name(computeTokenHeightDifference, "computeTokenHeightDifference");
function combineDistanceWithHeight(distance, heightDifference) {
  if (heightDifference <= 0) return distance;
  let nd = Math.min(distance, heightDifference);
  let ns = Math.abs(distance - heightDifference);
  const diagonals = safeGetGameSetting("core", "gridDiagonals");
  const GRID_DIAGONALS = CONST.GRID_DIAGONALS;
  let cd;
  switch (diagonals) {
    case GRID_DIAGONALS.EQUIDISTANT:
      cd = nd;
      break;
    case GRID_DIAGONALS.EXACT:
      cd = Math.SQRT2 * nd;
      break;
    case GRID_DIAGONALS.APPROXIMATE:
      cd = 1.5 * nd;
      break;
    case GRID_DIAGONALS.RECTILINEAR:
      cd = 2 * nd;
      break;
    case GRID_DIAGONALS.ALTERNATING_1:
      cd = (nd + 1 & -2) + (nd >> 1);
      break;
    case GRID_DIAGONALS.ALTERNATING_2:
      cd = (nd + 1 & -2) + (nd >> 1);
      break;
    case GRID_DIAGONALS.ILLEGAL:
      cd = 2 * nd;
      nd = 0;
      ns = distance + heightDifference;
      break;
    default:
      cd = nd;
      break;
  }
  return ns + cd;
}
__name(combineDistanceWithHeight, "combineDistanceWithHeight");
function computeDistance(t1, t2, options = { wallsBlock: false, includeCover: true }) {
  if (!canvas?.scene || !canvas.grid || !canvas.dimensions) return -1;
  t1 = getPlaceable(t1);
  t2 = getPlaceable(t2);
  if (!t1 || !t2) return -1;
  let { wallsBlock = false, includeCover = true, includeElevation = configSettings.optionalRules.distanceIncludesHeight } = options;
  const actor = t1.actor;
  const ignoreWallsFlag = foundry.utils.getProperty(actor, `flags.${MODULE_ID}.ignoreWalls`);
  if (ignoreWallsFlag) {
    wallsBlock = false;
  }
  let t1DocWidth = t1.document.width ?? 1;
  let t1DocHeight = t1.document.height ?? 1;
  let t2DocWidth = t2.document.width ?? 1;
  let t2DocHeight = t2.document.height ?? 1;
  const t1StartX = t1DocWidth >= 1 ? 0.5 : t1DocWidth / 2;
  const t1StartY = t1DocHeight >= 1 ? 0.5 : t1DocHeight / 2;
  const t2StartX = t2DocWidth >= 1 ? 0.5 : t2DocWidth / 2;
  const t2StartY = t2DocHeight >= 1 ? 0.5 : t2DocHeight / 2;
  const t1Elevation = t1.document.elevation ?? 0;
  const t2Elevation = t2.document.elevation ?? 0;
  const t1TopElevation = t1Elevation + Math.max(t1DocHeight, t1DocWidth) * (canvas.dimensions?.distance ?? 5);
  const t2TopElevation = t2Elevation + Math.min(t2DocHeight, t2DocWidth) * (canvas.dimensions?.distance ?? 5);
  const wallsBlockRange = configSettings.optionalRules.wallsBlockRange;
  const preCheck = wallsBlock ? computeWallBlockPreCheck(t1, t2, wallsBlock, includeCover) : { blocked: false, coverVisible: void 0 };
  if (preCheck.blocked) return -1;
  const coverVisible = preCheck.coverVisible;
  let segments = [];
  let rayDistance, distance;
  let heightDifference = 0;
  if (!(t2.document instanceof WallDocument)) {
    for (let x = t1StartX; x < t1DocWidth; x++) {
      for (let y = t1StartY; y < t1DocHeight; y++) {
        if (y === t1StartY + 1) {
          if (x > t1StartX && x < t1DocWidth - t1StartX) {
            y = t1DocHeight - t1StartY;
          }
        }
        const point1 = canvas.grid.getCenterPoint({ x: Math.round(t1.document.x + canvas.dimensions.size * x), y: Math.round(t1.document.y + canvas.dimensions.size * y) });
        const origin = new PIXI.Point(point1.x, point1.y);
        for (let x1 = t2StartX; x1 < t2DocWidth; x1++) {
          for (let y1 = t2StartY; y1 < t2DocHeight; y1++) {
            if (y1 === t2StartY + 1) {
              if (x1 > t2StartX && x1 < t2DocWidth - t2StartX) {
                y1 = t2DocHeight - t2StartY;
              }
            }
            const point2 = canvas.grid.getCenterPoint({ x: Math.round(t2.document.x + canvas.dimensions.size * x1), y: Math.round(t2.document.y + canvas.dimensions.size * y1) });
            const dest = new PIXI.Point(point2.x, point2.y);
            const r = new foundry.canvas.geometry.Ray(origin, dest);
            if (wallsBlock) {
              const collision = checkWallCollision(origin, dest, {
                source: t1.document,
                wallsBlockRange: configSettings.optionalRules.wallsBlockRange,
                coverVisible,
                originElevation: t1Elevation,
                destElevation: t2Elevation,
                originTopElevation: t1TopElevation,
                destTopElevation: t2TopElevation
              });
              if (collision) continue;
            }
            segments.push({ ray: r });
          }
        }
      }
    }
    if (segments.length === 0) {
      return -1;
    }
    rayDistance = segments.map((ray) => midiMeasureDistances([ray], { gridSpaces: true }));
    distance = Math.min(...rayDistance);
    if (includeElevation) {
      heightDifference = computeTokenHeightDifference(t1, t2);
    }
  } else {
    const w = t2.document;
    const closestPoint = foundry.utils.closestPointToSegment(t1.center, w.object.edge.a, w.object.edge.b);
    distance = midiMeasureDistances([{ ray: new foundry.canvas.geometry.Ray(t1.center, closestPoint) }], { gridSpaces: true });
    if (includeElevation) {
      if (!w.flags?.["wall-height"]) heightDifference = 0;
      else {
        const wh = w.flags?.["wall-height"];
        if (wh.top === null && wh.bottom === null) heightDifference = 0;
        else if (wh.top === null) heightDifference = Math.max(0, wh.bottom - t1Elevation);
        else if (wh.bottom === null) heightDifference = Math.max(0, t1Elevation - wh.top);
        else heightDifference = Math.max(0, wh.bottom - t1TopElevation, t1Elevation - wh.top);
      }
    }
  }
  if (includeElevation) {
    distance = combineDistanceWithHeight(distance, heightDifference);
  }
  return Math.max(distance, 0);
}
__name(computeDistance, "computeDistance");
var pointWarn = foundry.utils.debounce(() => {
  ui.notifications?.warn("4 Point LOS check selected but dnd5e-helpers not installed");
}, 100);
function checkActivityRange(activityIn, tokenRef, targetsRef, showWarning = true) {
  if (!canvas || !canvas.scene) return { result: "normal" };
  const checkRangeFunction = /* @__PURE__ */ __name((activity, token, targets) => {
    if (!canvas || !canvas.scene) return {
      result: "normal"
    };
    if (!activity.range) return {
      result: "normal"
    };
    if (!token) {
      if (debugEnabled > 0) warn(`checkRange | ${game.user?.name} no token selected cannot check range`);
      return {
        result: "fail",
        reason: `${game.user?.name} no token selected`
      };
    }
    let actor = token.actor;
    if (!(activity.range.value || activity.range.reach) && !activity.range.long && activity.range.units !== "touch") return {
      result: "normal",
      reason: "no range specified"
    };
    if (activity.target?.affects.type === "self") return {
      result: "normal",
      reason: "self attack",
      range: 0
    };
    if (!allAttackTypes.includes(activity.actionType) && !["creature", "ally", "enemy"].includes(activity.target?.affects.type)) return {
      result: "normal",
      reason: "not an attack"
    };
    const attackType = activity.actionType;
    let range2 = (activity.range?.value || activity.range?.reach) ?? 0;
    let longRange2 = activity.range?.long ?? 0;
    if (activity.actor?.system) {
      let conditionData;
      let rangeBonus = activity.actor?.flags?.[MODULE_ID]?.range?.[attackType] ?? "0";
      rangeBonus = rangeBonus + " + " + (activity.actor?.flags?.[MODULE_ID]?.range?.all ?? "0");
      if (rangeBonus !== "0 + 0") {
        conditionData = createConditionData({ item: activity.item, activity, actor: activity.actor, target: token });
        const bonusValue = evalCondition(rangeBonus, conditionData, { errorReturn: 0, async: false });
        range2 = Math.max(0, range2 + bonusValue);
      }
      ;
      let longRangeBonus = activity.actor?.flags?.[MODULE_ID]?.long?.[attackType] ?? "0";
      longRangeBonus = longRangeBonus + " + " + (activity.actor?.flags?.[MODULE_ID]?.long?.all ?? "0");
      if (longRangeBonus !== "0 + 0") {
        if (!conditionData)
          conditionData = createConditionData({ item: activity.item, actor: activity.actor, activity, target: token });
        const bonusValue = evalCondition(longRangeBonus, conditionData, { errorReturn: 0, async: false });
        longRange2 = Math.max(0, longRange2 + bonusValue);
      }
      ;
    }
    if (longRange2 > 0 && longRange2 < range2) longRange2 = range2;
    if (activity.range?.units) {
      switch (activity.range.units) {
        case "mi":
          if (["feet", "ft"].includes(canvas.scene?.grid.units?.toLocaleLowerCase())) {
            range2 *= 5280;
            longRange2 *= 5280;
          } else if (["yards", "yd", "yds"].includes(canvas.scene?.grid.units?.toLocaleLowerCase())) {
            range2 *= 1760;
            longRange2 *= 1760;
          }
          break;
        case "km":
          if (["meter", "m", "meters", "metre", "metres"].includes(canvas.scene?.grid.units?.toLocaleLowerCase())) {
            range2 *= 1e3;
            longRange2 *= 1e3;
          }
          break;
        // "none" "self" "ft" "m" "any" "spec" undefined
        default:
          break;
      }
    }
    if (range2 < longRange2 && activity.actionType === "rwak" && (actor?.flags?.dnd5e?.sharpShooter || actor?.flags?.[MODULE_ID]?.sharpShooter)) {
      const conditionData = createConditionData({ item: activity.item, actor, activity, target: targets.first() });
      let sharpShooterEnabled = evalCondition(actor?.flags?.dnd5e?.sharpShooter, conditionData);
      sharpShooterEnabled ||= evalCondition(actor?.flags?.[MODULE_ID]?.sharpShooter, conditionData);
      if (sharpShooterEnabled) range2 = longRange2;
    }
    if (activity.actionType === "rsak" && actor?.flags?.dnd5e?.spellSniper) {
      const conditionData = createConditionData({ item: activity.item, actor, activity, target: targets.first() });
      let enabled = evalCondition(actor.flags.dnd5e.spellSniper, conditionData);
      if (enabled) {
        range2 = 2 * range2;
        longRange2 = 2 * longRange2;
      }
    }
    if (activity.range.units === "touch") {
      range2 = canvas.dimensions?.distance ?? 5;
      if (activity.item.system.range?.reach) range2 = activity.item.system.range.reach;
      longRange2 = 0;
    }
    const meleeActions = /* @__PURE__ */ new Set(["mwak", "msak"]);
    const isMeleeAttack = meleeActions.has(activity.actionType);
    const hasThrownProperty = activity.item.labels.properties?.some((p) => p.abbr === "thr");
    if (isMeleeAttack && !hasThrownProperty) {
      longRange2 = 0;
    }
    const isMetric = game.settings.get("dnd5e", "metricLengthUnits") ?? false;
    for (let target of targets) {
      if (target === token) continue;
      if (configSettings.optionalRules.wallsBlockRange !== "none" && globalThis.MidiQOL.WallsBlockConditions.some((status) => hasCondition(target.actor, status))) {
        return {
          result: "fail",
          reason: `${actor?.name}'s has one or more of ${globalThis.MidiQOL.WallsBlockConditions} so can't be targeted`,
          range: range2,
          longRange: longRange2
        };
      }
      const ignoreTotalCover = activity.midiProperties?.ignoreFullCover;
      const coverOptions = {
        wallsBlock: configSettings.optionalRules.wallsBlockRange !== "none" && !ignoreTotalCover,
        includeCover: !ignoreTotalCover
      };
      const rawDistance = getDistance(token, target, coverOptions);
      if (rawDistance < 0) {
        log(`${target.name} is blocked by a wall`);
        return {
          result: "fail",
          reason: `${actor?.name}'s target is blocked by a wall`,
          range: range2,
          longRange: longRange2
        };
      }
      const distance = Math.max(0, parseFloat(rawDistance.toFixed(isMetric ? 1 : 0)));
      if (longRange2 !== 0 && distance > longRange2 || distance > range2 && longRange2 === 0) {
        log(`${target.name} is too far ${distance} from your character you cannot hit`);
        if (checkMechanic("checkRange") === "longdisadv" && ["rwak", "rsak"].includes(activity.actionType)) {
          return {
            result: "dis",
            reason: `${actor?.name}'s target is ${Math.round(distance * 10) / 10} away and your range is only ${longRange2 || range2}`,
            range: range2,
            longRange: longRange2
          };
        } else {
          return {
            result: "fail",
            reason: `${actor?.name}'s target is ${Math.round(distance * 10) / 10} away and your range is only ${longRange2 || range2}`,
            range: range2,
            longRange: longRange2
          };
        }
      }
      if (distance > range2) return {
        result: "dis",
        reason: `${actor?.name}'s target is ${Math.round(distance * 10) / 10} away and your range is only ${longRange2 || range2}`,
        range: range2,
        longRange: longRange2
      };
    }
    return {
      result: "normal",
      range: range2,
      longRange: longRange2
    };
  }, "checkRangeFunction");
  const tokenIn = getToken(tokenRef);
  const targetsIn = targetsRef?.map((t) => getToken(t)).filter((t) => !!t);
  if (!tokenIn || !targetsIn) return { result: "fail", attackingToken: void 0 };
  let attackingToken = tokenIn;
  if (!canvas || !canvas.tokens || !tokenIn || !targetsIn) return {
    result: "fail",
    attackingToken: tokenIn
  };
  let canOverride = tokenIn.actor?.flags?.[MODULE_ID]?.rangeOverride?.attack?.all || tokenIn.actor?.flags?.[MODULE_ID]?.rangeOverride?.attack?.[activityIn.actionType];
  if (typeof canOverride === "string") {
    const tokenInActor = tokenIn.actor;
    const conditionData = createConditionData({ item: activityIn.item, activity: activityIn, actor: tokenInActor });
    canOverride = evalCondition(canOverride, conditionData);
  }
  const { result, reason, range, longRange } = checkRangeFunction(activityIn, attackingToken, targetsIn);
  if (!canOverride) {
    if (result === "fail" && reason) {
      if (showWarning) ui.notifications?.warn(reason);
    }
    return { result, attackingToken, range, longRange };
  }
  const ownedTokens = canvas.tokens.ownedTokens;
  let possibleAttackers = ownedTokens.filter((t) => {
    let canOverride2 = t.actor?.flags?.[MODULE_ID]?.rangeOverride?.attack?.all || t.actor?.flags?.[MODULE_ID]?.rangeOverride?.attack?.[activityIn.actionType];
    if (typeof canOverride2 === "string") {
      const tActor = t.actor;
      const conditionData = createConditionData({ item: activityIn.item, activity: activityIn, actor: tActor });
      canOverride2 = evalCondition(canOverride2, conditionData);
    }
    return canOverride2;
  });
  const successToken = possibleAttackers.find((attacker) => checkRangeFunction(activityIn, attacker, targetsIn).result === "normal");
  if (successToken) return { result: "normal", attackingToken: successToken, range, longRange };
  return { result: "fail", attackingToken, range, longRange };
}
__name(checkActivityRange, "checkActivityRange");
function getLevelsAutoCoverOptions() {
  const options = {
    //@ts-expect-error
    tokensProvideCover: game.settings.get("levelsautocover", "tokensProvideCover"),
    //@ts-expect-error
    ignoreFriendly: game.settings.get("levelsautocover", "ignoreFriendly"),
    //@ts-expect-error
    copsesProvideCover: game.settings.get("levelsautocover", "copsesProvideCover"),
    //@ts-expect-error
    tokenCoverAA: game.settings.get("levelsautocover", "tokenCoverAA"),
    //@ts-expect-error
    precision: game.settings.get("levelsautocover", "coverRestriction")
  };
  return options;
}
__name(getLevelsAutoCoverOptions, "getLevelsAutoCoverOptions");
var TOTAL_COVER = Infinity;
var THREE_QUARTERS_COVER = 5;
var HALF_COVER = 2;
function initCoverConstants() {
  THREE_QUARTERS_COVER = CONFIG.DND5E?.statusEffects?.coverThreeQuarters?.coverBonus ?? 5;
  HALF_COVER = CONFIG.DND5E?.statusEffects?.coverHalf?.coverBonus ?? 2;
}
__name(initCoverConstants, "initCoverConstants");
function hasCoverTotal(actor) {
  return !!actor?.statuses?.has("coverTotal");
}
__name(hasCoverTotal, "hasCoverTotal");
function getCoverBonus(attacker, target, activity) {
  const targetActor = target instanceof TokenDocument ? target.actor : target?.actor;
  if (hasCoverTotal(targetActor)) return TOTAL_COVER;
  const statusCoverBonus = targetActor?.system?.attributes?.ac?.cover ?? 0;
  const moduleCoverBonus = computeCoverBonus(attacker, target, activity);
  return Math.max(statusCoverBonus, moduleCoverBonus);
}
__name(getCoverBonus, "getCoverBonus");
function computeCoverBonus(attackerIn, targetIn, activity) {
  const attacker = attackerIn instanceof TokenDocument ? attackerIn.object : attackerIn;
  const target = targetIn instanceof TokenDocument ? targetIn.object : targetIn;
  if (activity instanceof Item) {
    const item = activity;
    activity = void 0;
    foundry.utils.logCompatibilityWarning("computeCoverBonus(attacker, target, item) is deprecated in favor of computeCoverBonus(attacker, target, activity)");
  }
  if (!attacker || !target) return 0;
  let coverBonus = 0;
  try {
    switch (configSettings.optionalRules.coverCalculation) {
      case "levelsautocover":
        if (!installedModules.get("levelsautocover") || !game.settings.get("levelsautocover", "apiMode") || !AutoCover) return 0;
        const coverData = AutoCover.calculateCover(attacker, target);
        const coverDetail = AutoCover.getCoverData();
        if (coverData.rawCover === 0) coverBonus = TOTAL_COVER;
        else if (coverData.rawCover > coverDetail[1].percent) coverBonus = 0;
        else if (coverData.rawCover < coverDetail[0].percent) coverBonus = THREE_QUARTERS_COVER;
        else if (coverData.rawCover < coverDetail[1].percent) coverBonus = HALF_COVER;
        if (coverData.obstructingToken) coverBonus = Math.max(2, coverBonus);
        break;
      case "simbuls-cover-calculator":
        if (!installedModules.get("simbuls-cover-calculator")) return 0;
        if (globalThis.CoverCalculator) {
          const coverData2 = globalThis.CoverCalculator.Cover(attacker, target);
          if (attacker === target) {
            coverBonus = 0;
            break;
          }
          if (coverData2?.data?.results.cover === 3) coverBonus = TOTAL_COVER;
          else coverBonus = -coverData2?.data?.results.value || 0;
        }
        break;
      case "simplecover5e":
        const simpleCoverApi = game.modules.get("simplecover5e")?.api;
        if (!installedModules.get("simplecover5e") || !simpleCoverApi) return 0;
        if (attacker !== target && configSettings.optionalRules.wallsBlockRange !== "none") {
          const preCheck = computeWallBlockPreCheck(attacker, target, true, true);
          if (preCheck.blocked) {
            coverBonus = TOTAL_COVER;
            break;
          }
        }
        const coverOptions = {
          attacker,
          target,
          scene: attacker.scene,
          losCheck: true
        };
        if (activity) coverOptions.activity = activity;
        const coverResult = simpleCoverApi.getCover(coverOptions);
        if (!coverResult || coverResult.bonus == null) return TOTAL_COVER;
        coverBonus = coverResult.bonus;
        break;
      case "tokencover":
        if (!installedModules.get("tokencover")) coverBonus = 0;
        else {
          const coverValue = calcTokenCover(attacker, target);
          if (coverValue === 4 || coverValue === 3) coverBonus = TOTAL_COVER;
          else if (coverValue === 2) coverBonus = THREE_QUARTERS_COVER;
          else if (coverValue === 1) coverBonus = HALF_COVER;
          else coverBonus = 0;
        }
        break;
      case "none":
      default:
        coverBonus = 0;
        break;
    }
    return coverBonus;
  } catch (err) {
    const message = "Error in computeCoverBonus";
    error2(message, err);
    TroubleShooter.recordError(err, message);
    return 0;
  }
}
__name(computeCoverBonus, "computeCoverBonus");
function computeTargetAC(attacker, target, activity, options = {}) {
  const targetActor = target.actor;
  const item = activity?.item;
  const { ignoreCover = false, flankingMod = 0, targetACMod = 0 } = options;
  const acValue = targetActor?.system?.attributes?.ac?.value ?? 10;
  let statusCoverBonus = targetActor?.system?.attributes?.ac?.cover ?? 0;
  const baseAC = acValue - statusCoverBonus;
  if (hasCoverTotal(targetActor)) statusCoverBonus = TOTAL_COVER;
  const moduleCoverBonus = computeCoverBonus(attacker, target, activity);
  let effectiveCoverBonus = Math.max(statusCoverBonus, moduleCoverBonus);
  let appliedCoverDelta = Math.max(moduleCoverBonus - statusCoverBonus, 0);
  if (activity?.midiProperties?.ignoreFullCover) {
    if (item?.type === "spell") {
      effectiveCoverBonus = 0;
      appliedCoverDelta = 0;
    } else if (effectiveCoverBonus === TOTAL_COVER) {
      effectiveCoverBonus = THREE_QUARTERS_COVER;
      appliedCoverDelta = Math.max(THREE_QUARTERS_COVER - statusCoverBonus, 0);
    }
  }
  if ((activity?.actionType ?? item?.system?.actionType) === "rwak" && attacker.actor && effectiveCoverBonus !== TOTAL_COVER && effectiveCoverBonus !== 0 && (attacker.actor.flags?.[MODULE_ID]?.sharpShooter || attacker.actor.flags?.dnd5e?.sharpShooter)) {
    const conditionData = createConditionData({ item, actor: attacker.actor, activity, target });
    let enabled = evalCondition(attacker.actor.flags?.dnd5e?.sharpShooter, conditionData);
    enabled ||= evalCondition(attacker.actor.flags?.[MODULE_ID]?.sharpShooter, conditionData);
    if (enabled) {
      effectiveCoverBonus = 0;
      appliedCoverDelta = 0;
    }
  }
  if (["rsak"].includes(activity?.actionType ?? item?.system?.actionType) && attacker.actor && attacker.actor.flags?.dnd5e?.spellSniper && effectiveCoverBonus !== TOTAL_COVER && effectiveCoverBonus !== 0) {
    const conditionData = createConditionData({ item, actor: attacker.actor, activity, target });
    const enabled = evalCondition(attacker.actor.flags.dnd5e.spellSniper, conditionData);
    if (enabled) {
      effectiveCoverBonus = 0;
      appliedCoverDelta = 0;
    }
  }
  let coverIgnored = false;
  if (ignoreCover && effectiveCoverBonus < TOTAL_COVER) {
    coverIgnored = true;
    effectiveCoverBonus = 0;
    appliedCoverDelta = 0;
  }
  let effectiveAC = acValue + targetACMod;
  if (coverIgnored) {
    effectiveAC -= statusCoverBonus;
  } else {
    effectiveAC += appliedCoverDelta;
  }
  effectiveAC -= flankingMod;
  if (effectiveCoverBonus === TOTAL_COVER) effectiveAC = Infinity;
  const result = {
    effectiveAC,
    baseAC,
    statusCoverBonus,
    moduleCoverBonus,
    appliedCoverDelta,
    effectiveCoverBonus,
    coverIgnored,
    flankingMod,
    targetACMod
  };
  return result;
}
__name(computeTargetAC, "computeTargetAC");
function isAutoFastForward2(rollType, { workflow, user } = {}) {
  if (rollType === "attack") {
    if (workflow?.rollOptions?.fastForwardAttack !== void 0) return workflow.rollOptions.fastForwardAttack;
    if (workflow?.workflowOptions?.autoFastAttack !== void 0) return workflow.workflowOptions.autoFastAttack;
    if (workflow?.workflowType === "DummyWorkflow") return !!workflow.rollOptions.fastForward;
  } else if (rollType === "damage") {
    if (workflow?.rollOptions?.fastForwardDamage !== void 0) return workflow.rollOptions.fastForwardDamage;
    if (workflow?.workflowOptions?.fastForwardDamage !== void 0) return workflow.workflowOptions.fastForwardDamage;
    if (workflow?.workflowType === "DummyWorkflow") return !!workflow.rollOptions.fastForwardDamage;
  }
  return (user ?? game.user)?.isGM ? configSettings.gmAutoFastForward.includes(rollType) : configSettings.autoFastForward.includes(rollType);
}
__name(isAutoFastForward2, "isAutoFastForward");
function autoConsumeResource(workflow) {
  if (workflow?.workflowOptions?.autoConsumeResource !== void 0) return workflow.workflowOptions.autoConsumeResource;
  return game.user?.isGM ? configSettings.gmConsumeResource : configSettings.consumeResource;
}
__name(autoConsumeResource, "autoConsumeResource");
function getConsumptionData(source) {
  if (!source) return void 0;
  if (source.system?.deltas != null) return source.system.deltas;
  if (typeof source.getFlag === "function") return source.getFlag("dnd5e", "use.consumed");
  return source.data?.system?.deltas ?? source.data?.flags?.dnd5e?.use?.consumed;
}
__name(getConsumptionData, "getConsumptionData");
function getAutoRollDamage(workflow) {
  if (workflow?.actor?.type === configSettings.averageDamage || configSettings.averageDamage === "all") return "onHit";
  if (workflow?.workflowOptions?.autoRollDamage) {
    const damageOptions = Object.keys(autoRollDamageOptions);
    if (damageOptions.includes(workflow.workflowOptions.autoRollDamage))
      return workflow.workflowOptions.autoRollDamage;
    console.warn(`midi-qol | getAutoRollDamage | could not find ${workflow.workflowOptions.autoRollDamage} workflowOptions.autoRollDamage must be ond of ${damageOptions} defaulting to "onHit"`);
    return "onHit";
  }
  return game.user?.isGM ? configSettings.gmAutoDamage : configSettings.autoRollDamage;
}
__name(getAutoRollDamage, "getAutoRollDamage");
function getAutoRollAttack(workflow) {
  if (workflow?.systemCard) return false;
  if (workflow?.workflowOptions?.autoRollAttack !== void 0) {
    return workflow.workflowOptions.autoRollAttack;
  }
  return game.user?.isGM ? configSettings.gmAutoAttack : configSettings.autoRollAttack;
}
__name(getAutoRollAttack, "getAutoRollAttack");
function getRemoveAllButtons(activity) {
  if (activity) {
    const activitySetting = activity.midiProperties?.removeChatButtons;
    if (activitySetting && activitySetting !== "default") {
      return activitySetting === "everything";
    }
  }
  return game.user?.isGM ? configSettings.gmRemoveButtons === "everything" : configSettings.removeButtons === "everything";
}
__name(getRemoveAllButtons, "getRemoveAllButtons");
function getRemoveAttackButtons(activity) {
  if (activity) {
    const activitySetting = activity.midiProperties?.removeChatButtons;
    if (activitySetting) {
      if (["all", "attack", "everything"].includes(activitySetting)) return true;
      if (activitySetting !== "default") return false;
    }
  }
  return game.user?.isGM ? ["all", "attack", "everything"].includes(configSettings.gmRemoveButtons) : ["all", "attack", "everything"].includes(configSettings.removeButtons);
}
__name(getRemoveAttackButtons, "getRemoveAttackButtons");
function getRemoveDamageButtons(activity) {
  if (activity) {
    const activitySetting = activity.midiProperties?.removeChatButtons;
    if (activitySetting) {
      if (["all", "damage", "everything"].includes(activitySetting)) return true;
      if (activitySetting !== "default") return false;
    }
  }
  return game.user?.isGM ? ["all", "damage", "everything"].includes(configSettings.gmRemoveButtons) : ["all", "damage", "everything"].includes(configSettings.removeButtons);
}
__name(getRemoveDamageButtons, "getRemoveDamageButtons");
function getReactionSetting(player) {
  if (!player) return "none";
  return player.isGM ? configSettings.gmDoReactions : configSettings.doReactions;
}
__name(getReactionSetting, "getReactionSetting");
function getTokenPlayerName(token, checkGM = false) {
  if (game.user) return getTokenPlayerNameForUser(game.user, token, checkGM);
  return getTokenName(token);
}
__name(getTokenPlayerName, "getTokenPlayerName");
function getTokenPlayerNameForUser(user, token, checkGM = false) {
  if (!token) return game.user?.name;
  let name = getTokenName(token);
  if (checkGM && user?.isGM) return name;
  if (game.modules.get("anonymous")?.active) {
    const api = game.modules.get("anonymous")?.api;
    if (api?.playersSeeName(token.actor)) return name;
    else return api?.getName(token.actor);
  } else if (game.modules.get("hide-npc-names")?.active) {
    return getPlayerNPCNameHideNPCNAmes(token);
  }
  return name;
}
__name(getTokenPlayerNameForUser, "getTokenPlayerNameForUser");
function getPlayerNPCNameHideNPCNAmes(token) {
  const actorFlags = foundry.utils.getProperty(token.actor ?? {}, "flags.hide-npc-names");
  if (actorFlags?.nameHiddenOverride === true) return actorFlags.replacementNameOverride;
  if (actorFlags?.nameHiddenOverride === false) return getTokenName(token);
  if (actorFlags === void 0) {
    let hideSetting;
    switch ((token instanceof TokenDocument ? token : token.document).disposition) {
      case CONST.TOKEN_DISPOSITIONS.FRIENDLY:
        hideSetting = safeGetGameSetting("hide-npc-names", "hideFriendlyNames");
        if (hideSetting) return safeGetGameSetting("hide-npc-names", "friendlyNameReplacement");
        break;
      case CONST.TOKEN_DISPOSITIONS.HOSTILE:
        hideSetting = safeGetGameSetting("hide-npc-names", "hideHostileNames");
        if (hideSetting) return safeGetGameSetting("hide-npc-names", "hostileNameReplacement");
        break;
      case CONST.TOKEN_DISPOSITIONS.SECRET:
        hideSetting = safeGetGameSetting("hide-npc-names", "hideSecretNames");
        if (hideSetting) return safeGetGameSetting("hide-npc-names", "secretNameReplacement");
        break;
      case CONST.TOKEN_DISPOSITIONS.NEUTRAL:
        hideSetting = safeGetGameSetting("hide-npc-names", "hideNeutralNames");
        if (hideSetting) return safeGetGameSetting("hide-npc-names", "neutralNameReplacement");
        break;
    }
  }
  return getTokenName(token);
}
__name(getPlayerNPCNameHideNPCNAmes, "getPlayerNPCNameHideNPCNAmes");
function getSpeaker(actor) {
  const speaker = ChatMessage.getSpeaker({ actor });
  if (!configSettings.useTokenNames) return speaker;
  let token = actor.token;
  if (!token) token = actor.getActiveTokens(false, true)[0];
  if (token) speaker.alias = token.name;
  return speaker;
}
__name(getSpeaker, "getSpeaker");
function mapTokenString(disposition) {
  if (typeof disposition === "number") return disposition;
  if (disposition.toLocaleLowerCase().trim() === i18n("TOKEN.DISPOSITION.FRIENDLY")?.toLocaleLowerCase()) return 1;
  else if (disposition.toLocaleLowerCase().trim() === i18n("TOKEN.DISPOSITION.HOSTILE")?.toLocaleLowerCase()) return -1;
  else if (disposition.toLocaleLowerCase().trim() === i18n("TOKEN.DISPOSITION.NEUTRAL")?.toLocaleLowerCase()) return 0;
  else if (disposition.toLocaleLowerCase().trim() === i18n("TOKEN.DISPOSITION.SECRET")?.toLocaleLowerCase()) return -2;
  else if (disposition.toLocaleLowerCase().trim() === i18n("all")?.toLocaleLowerCase()) return null;
  const validStrings = ["TOKEN.DISPOSITION.FRIENDLY", "TOKEN.DISPOSITION.HOSTILE", "TOKEN.DISPOSITION.NEUTRAL", "TOKEN.DISPOSITION.SECRET", "all"].map((s) => i18n(s));
  throw new Error(`Midi-qol | findNearby ${disposition} is invalid. Disposition must be one of "${validStrings}"`);
}
__name(mapTokenString, "mapTokenString");
function findNearbyCount(disposition, token, distance, options = {
  includeIncapacitated: false,
  canSee: false,
  isSeen: false,
  includeToken: false,
  relative: true
}) {
  return findNearby(disposition, token, distance, options)?.length ?? 0;
}
__name(findNearbyCount, "findNearbyCount");
function findNearby(disposition, tokenRef, distance, options = {
  includeIncapacitated: false,
  canSee: false,
  isSeen: false,
  includeToken: false,
  relative: true
}) {
  const token = getToken(tokenRef);
  if (!token) return [];
  if (!canvas || !canvas.scene) return [];
  try {
    if (!(token instanceof Token2)) {
      throw new Error("find nearby token is not of type token or the token uuid is invalid");
    }
    ;
    let relative = options.relative ?? true;
    let targetDisposition;
    if (typeof disposition === "string") disposition = mapTokenString(disposition);
    if (disposition instanceof Array) {
      if (disposition.some((s) => s === "all")) disposition = [-1, 0, 1];
      else disposition = disposition.map((s) => mapTokenString(s) ?? 0);
      targetDisposition = disposition.map((i) => typeof i === "number" && [-1, 0, 1].includes(i) && relative ? token.document.disposition * i : Number.isNumeric(i) ? Number(i) : 0);
    } else if (typeof disposition === "number" && [-1, 0, 1].includes(disposition)) {
      targetDisposition = relative ? [token.document.disposition * disposition] : [disposition];
    } else targetDisposition = [CONST.TOKEN_DISPOSITIONS.HOSTILE, CONST.TOKEN_DISPOSITIONS.NEUTRAL, CONST.TOKEN_DISPOSITIONS.FRIENDLY];
    const canvasPlaceables = canvas.tokens?.placeables ?? [];
    let nearby = canvasPlaceables.filter((t) => {
      const tActor = t.actor;
      const tDocument = t.document;
      if (!isValidTarget(t)) return false;
      if (options.maxSize && (tDocument.height ?? 1) * (tDocument.width ?? 1) > options.maxSize) return false;
      if (!options.includeIncapacitated && checkIncapacitated(tActor, debugEnabled > 0, false)) return false;
      let inRange = false;
      if (t.actor && (t.id !== token.id || options?.includeToken) && // not the token
      (disposition === null || targetDisposition.includes(t.document.disposition))) {
        const tokenDistance = getDistance(t, token, { wallsBlock: true });
        inRange = 0 <= tokenDistance && tokenDistance <= distance;
      } else return false;
      if (inRange && options.canSee && !canSense(t, token)) return false;
      if (inRange && options.isSeen && !canSense(token, t)) return false;
      return inRange;
    });
    return nearby ?? [];
  } catch (err) {
    TroubleShooter.recordError(err, "findnearby error");
    error2(err);
    return [];
  }
}
__name(findNearby, "findNearby");
function checkNearby(disposition, tokenRef, distance, options = {
  includeIncapacitated: false,
  canSee: false,
  isSeen: false,
  includeToken: false,
  relative: true
}) {
  const token = getToken(tokenRef);
  const tokenDisposition = token?.document.disposition;
  if (tokenDisposition === 0) options.relative = false;
  if (!token) return false;
  return findNearby(disposition, token, distance, options).length !== 0;
}
__name(checkNearby, "checkNearby");
function hasCondition(actorRef, condition) {
  let actor = getActor(actorRef);
  if (!actor) return 0;
  if (!actor.system.traits || !actor.statuses) return 0;
  if (actor.system.traits.ci?.value?.has(condition)) return 0;
  if (actor.statuses.has(condition)) return 1;
  const specials = CONFIG.specialStatusEffects;
  switch (condition?.toLocaleLowerCase()) {
    case "blind":
      if (actor.statuses.has(specials.BLIND)) return 1;
      break;
    case "burrow":
    case "burrowing":
      if (actor.statuses.has(specials.BURROW)) return 1;
      break;
    case "dead":
      if (actor.statuses.has(specials.DEFEATED)) return 1;
      break;
    case "deaf":
      if (actor.statuses.has(specials.DEAF)) return 1;
      break;
    case "disease":
    case "diseased":
      if (actor.statuses.has(specials.DISEASE)) return 1;
      break;
    case "fly":
    case "flying":
      if (actor.statuses.has(specials.FLY)) return 1;
      break;
    case "hidden":
    case "hiding":
      if (actor.statuses.has("hidden") || actor.statuses.has("hiding")) return 1;
      break;
    case "inaudible":
    case "silent":
      if (actor.statuses.has(specials.INAUDIBLE)) return 1;
      break;
    case "invisible":
      if (actor.statuses.has(specials.INVISIBLE)) return 1;
      break;
    case "poison":
    case "poisoned":
      if (actor.statuses.has(specials.POISON)) return 1;
      break;
  }
  if (actor.statuses.has(condition.toLocaleLowerCase()) || actor.statuses.has(condition)) return 1;
  return 0;
}
__name(hasCondition, "hasCondition");
async function removeInvisible() {
  if (!canvas || !canvas.scene) return;
  const token = this.attackingToken ?? fromUuidSync(this.tokenUuid);
  removeInvisibleCondition(token);
}
__name(removeInvisible, "removeInvisible");
async function removeInvisibleCondition(tokenRef) {
  const token = getToken(tokenRef);
  if (!token) return;
  await removeTokenConditionEffect(token, i18n(`midi-qol.invisible`) ?? "");
  const invisId = CONFIG.specialStatusEffects.INVISIBLE ?? "invisible";
  if (CONFIG.statusEffects.find((se) => se.id === invisId)) {
    await token.actor?.toggleStatusEffect(invisId, { active: false });
  }
  if (debugEnabled > 0) log(`Invisibility removed for ${token.name}`);
}
__name(removeInvisibleCondition, "removeInvisibleCondition");
async function removeHidden() {
  if (!canvas?.scene) return;
  const token = this.attackingToken ?? fromUuidSync(this.tokenUuid);
  if (!token) return;
  removeHiddenCondition(token);
}
__name(removeHidden, "removeHidden");
async function removeHiddenCondition(tokenRef) {
  const token = getToken(tokenRef);
  if (!token) return;
  if (!token.actor) return;
  if (CONFIG.statusEffects.find((se) => se.id === "hidden")) {
    await token.actor.toggleStatusEffect("hidden", { active: false });
  }
  if (CONFIG.statusEffects.find((se) => se.id === "hiding")) {
    await token.actor.toggleStatusEffect("hiding", { active: false });
  }
  await removeTokenConditionEffect(token, i18n(`midi-qol.hidden`) ?? "");
  if (installedModules.get("perceptive")) {
    const api = game.modules.get("perceptive")?.api;
    api?.PerceptiveFlags.setPerceptiveStealthing(token.document, false);
  }
  if (debugEnabled > 0) log(`Hidden removed for ${token.name}`);
}
__name(removeHiddenCondition, "removeHiddenCondition");
async function removeTokenConditionEffect(token, condition) {
  if (!token) return;
  const hasEffect = token.actor?.appliedEffects.find((ef) => ef.name === condition);
  if (hasEffect) await expireEffects(token.actor, [hasEffect], { "expiry-reason": `midi-qol:removeTokenCondition:${condition}` });
}
__name(removeTokenConditionEffect, "removeTokenConditionEffect");
async function expireMyEffects(effectsToExpire) {
  if (this.item?.flags?.[MODULE_ID]?.isConcentrationCheck) return;
  const expireHit = effectsToExpire.includes("1Hit") && !this.effectsAlreadyExpired.includes("1Hit");
  const expireCritical = effectsToExpire.includes("1Critical") && !this.effectsAlreadyExpired.includes("1Critical");
  const expireFumble = effectsToExpire.includes("1Fumble") && !this.effectsAlreadyExpired.includes("1Fumble");
  let expireAnyAction = effectsToExpire.includes("1Action") && !this.effectsAlreadyExpired.includes("1Action");
  const expireBonusAction = (effectsToExpire.includes("1Action") || effectsToExpire.includes("Bonus Action")) && !this.effectsAlreadyExpired.includes("Bonus Action");
  const expireReaction = (effectsToExpire.includes("1Action") || effectsToExpire.includes("Reaction")) && !this.effectsAlreadyExpired.includes("Reaction");
  const expireTurnAction = (effectsToExpire.includes("1Action") || effectsToExpire.includes("Turn Action")) && !this.effectsAlreadyExpired.includes("Turn Action");
  const expireSpell = effectsToExpire.includes("1Spell") && !this.effectsAlreadyExpired.includes("1Spell");
  const expireAttack = effectsToExpire.includes("1Attack") && !this.effectsAlreadyExpired.includes("1Attack");
  const expireDamage = effectsToExpire.includes("DamageDealt") && !this.effectsAlreadyExpired.includes("DamageDealt");
  const expireInitiative = effectsToExpire.includes("Initiative") && !this.effectsAlreadyExpired.includes("Initiative");
  expireAnyAction ||= expireBonusAction || expireReaction || expireTurnAction;
  if (expireAnyAction) effectsToExpire.push("1Action");
  let allEffects = getAppliedEffects(this.actor, { includeEnchantments: true });
  const myExpiredEffects = allEffects?.filter((ef) => {
    const specialDuration = foundry.utils.getProperty(ef.flags, "dae.specialDuration");
    if (!specialDuration || !specialDuration?.length) return false;
    return expireAnyAction && specialDuration.includes("1Action") || expireBonusAction && specialDuration.includes("Bonus Action") && this.activity.activation.type === "bonus" || expireReaction && specialDuration.includes("Reaction") && this.activity.activation.type === "reaction" || expireTurnAction && specialDuration.includes("Turn Action") && this.activity.activation.type === "action" || expireAttack && this.activity?.hasAttack && specialDuration.includes("1Attack") || // @ts-expect-error no dnd5e-types
    expireSpell && this.item?.type === "spell" && specialDuration.includes("1Spell") || expireAttack && this.activity?.hasAttack && specialDuration.includes(`1Attack:${this.activity?.actionType}`) || expireHit && this.activity?.hasAttack && specialDuration.includes("1Hit") && this.hitTargets.size > 0 || expireHit && this.activity?.hasAttack && specialDuration.includes(`1Hit:${this.activity?.actionType}`) && this.hitTargets.size > 0 || expireCritical && this.activity?.hasAttack && specialDuration.includes("1Critical") && this.isCritical || expireFumble && this.activity?.hasAttack && specialDuration.includes("1Fumble") && this.isFumble || expireDamage && this.activity?.hasDamage && specialDuration.includes("DamageDealt") || expireInitiative && specialDuration.includes("Initiative");
  });
  if (debugEnabled > 1) debug("expire my effects", myExpiredEffects, expireAnyAction, expireAttack, expireHit);
  this.effectsAlreadyExpired = this.effectsAlreadyExpired.concat(effectsToExpire);
  if (myExpiredEffects?.length > 0) await expireEffects(this.actor, myExpiredEffects, { "expiry-reason": `midi-qol:${effectsToExpire}` });
}
__name(expireMyEffects, "expireMyEffects");
async function expireRollEffect(rolltype, abilityId, success) {
  const rollType = rolltype.charAt(0).toUpperCase() + rolltype.slice(1);
  const expiredEffects = this.appliedEffects?.filter((ef) => {
    const specialDuration = ef.flags?.dae?.specialDuration;
    if (!specialDuration) return false;
    if (specialDuration.includes(`is${rollType}`)) return true;
    if (specialDuration.includes(`is${rollType}.${abilityId}`)) return true;
    if (success === true && specialDuration.includes(`is${rollType}Success`)) return true;
    if (success === true && specialDuration.includes(`is${rollType}Success.${abilityId}`)) return true;
    if (success === false && specialDuration.includes(`is${rollType}Failure`)) return true;
    if (success === false && specialDuration.includes(`is${rollType}Failure.${abilityId}`)) return true;
    return false;
  }).map((ef) => ef.uuid);
  if (expiredEffects?.length > 0) {
    await timedAwaitExecuteAsGM("removeEffectUuids", {
      actorUuid: this.uuid,
      effects: expiredEffects,
      options: { "expiry-reason": `midi:special-duration:${rollType}:${abilityId}` }
    });
  }
}
__name(expireRollEffect, "expireRollEffect");
function validTargetTokens(tokenSet) {
  if (!tokenSet) return /* @__PURE__ */ new Set();
  return tokenSet.map((tk) => tk instanceof TokenDocument ? tk.object : tk).filter((tk) => !!tk?.actor && isValidTarget(tk));
}
__name(validTargetTokens, "validTargetTokens");
function fromActorUuid(uuid) {
  let doc = fromUuidSync(uuid);
  if (doc instanceof Actor) return doc;
  if (doc instanceof TokenDocument) return doc.actor;
  return null;
}
__name(fromActorUuid, "fromActorUuid");
function actorFromUuid(uuid) {
  let doc = fromUuidSync(uuid);
  if (doc instanceof Actor) return doc;
  if (doc instanceof TokenDocument) return doc.actor;
  if (doc instanceof Item) return doc.parent;
  if (doc instanceof ActiveEffect) {
    if (doc.parent instanceof Actor) return doc.parent;
    if (doc.parent?.parent instanceof Actor) return doc.parent.parent;
  }
  return null;
}
__name(actorFromUuid, "actorFromUuid");
var RollModifyDialog = class extends HandlebarsApplicationMixin12(ApplicationV211) {
  static {
    __name(this, "RollModifyDialog");
  }
  rollExpanded;
  timeRemaining;
  timeoutId;
  secondTimeoutId;
  aborted = false;
  data;
  constructor(data) {
    super(data);
    this.data = data;
    this.timeRemaining = this.data.timeout;
    this.rollExpanded = false;
    if (!data.rollMode) data.rollMode = safeGetGameSetting("core", "rollMode");
    this.timeoutId = setTimeout(() => {
      if (this.secondTimeoutId) clearTimeout(this.secondTimeoutId);
      this.timeoutId = void 0;
      this.close();
    }, this.data.timeout * 1e3);
    this.set1SecondTimeout();
  }
  static PARTS = {
    dialog: {
      id: "dialog-optional",
      classes: ["dialog", "midi-qol", "optional"],
      template: "modules/midi-qol/templates/dialog.hbs"
    }
  };
  static DEFAULT_OPTIONS = {
    window: {
      resizable: true
    },
    position: {
      height: "auto",
      width: 400
    }
  };
  get title() {
    let maxPad = 1;
    if (this.data.timeout < maxPad) maxPad = this.data.timeout;
    if (this.data.timeout) {
      const padCount = Math.ceil(this.timeRemaining / (this.data.timeout ?? defaultTimeout) * maxPad);
      const pad = "-".repeat(padCount);
      return `${this.data.title ?? "Dialog"} ${pad} ${this.timeRemaining}`;
    } else return this.data.title ?? "Dialog";
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    for (const button of Array.from(this.element.querySelectorAll(".dialog-button"))) {
      button.addEventListener("click", this._onClickButton.bind(this));
    }
    for (const diceRoll of Array.from(this.element.querySelectorAll(".dice-roll"))) {
      diceRoll.addEventListener("click", this._onDiceRollClick.bind(this));
    }
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    const flagOwners = this.data.flagOwners ?? {};
    const ownerOf = /* @__PURE__ */ __name((flagName) => flagOwners[flagName] ?? this.data.actor, "ownerOf");
    this.data.flags = this.data.flags.filter((flagName) => {
      const owner = ownerOf(flagName);
      if (getOptionalCountRemaining(this.data.actor, `${flagName}.count`, owner) < 1) return false;
      return foundry.utils.getProperty(owner, flagName) !== void 0;
    });
    if (this.data.flags.length === 0) this.close();
    this.data.buttons = this.data.flags.reduce((obj, flag) => {
      const owner = ownerOf(flag);
      let flagData = foundry.utils.getProperty(owner ?? {}, flag);
      let value = foundry.utils.getProperty(flagData ?? {}, this.data.flagSelector);
      if (typeof value === "object") value = void 0;
      let icon = "fas fa-dice-d20";
      if (value !== void 0) {
        let labelDetail;
        if (typeof value === "string") {
          labelDetail = Roll.replaceFormulaData(value, this.data.actor.getRollData());
          if (value.startsWith("ItemMacro")) {
            icon = CONFIG.Macro.sidebarIcon;
            if (value === "ItemMacro")
              labelDetail = this.data.item?.name ?? "Macro";
            else {
              const uuid = value.split(".").slice(1).join(".");
              const item = fromUuidSync(uuid);
              if (item)
                labelDetail = item.name;
              else
                labelDetail = uuid;
            }
          } else if (value.startsWith("function")) {
            icon = CONFIG.Macro.sidebarIcon;
            labelDetail = value.split(".").slice(-1);
          } else if (value.startsWith("Macro")) {
            icon = CONFIG.Macro.sidebarIcon;
            labelDetail = value.split(".").slice(1).join(".");
          }
        } else labelDetail = `${value}`;
        obj[foundry.utils.randomID()] = {
          icon: `<i class="${icon}"></i>`,
          //          label: (flagData.label ?? "Bonus") + `  (${foundry.utils.getProperty(flagData, this.data.flagSelector) ?? "0"})`,
          label: (flagData?.label ?? "Bonus") + ` (${labelDetail})`,
          value: `${value}`,
          key: flag,
          callback: this.data.callback
        };
      }
      let selector = this.data.flagSelector.split(".");
      if (selector[selector.length - 1] !== "all") {
        selector[selector.length - 1] = "all";
        const allSelector = selector.join(".");
        value = foundry.utils.getProperty(flagData ?? {}, allSelector);
        if (typeof value === "object") value = void 0;
        if (value !== void 0) {
          if (typeof value !== "string") value = `${value}`;
          let labelDetail = Roll.replaceFormulaData(value, this.data.actor.getRollData());
          if (value.startsWith("ItemMacro")) {
            icon = CONFIG.Macro.sidebarIcon;
            if (value === "ItemMacro")
              labelDetail = this.data.item?.name ?? "Macro";
            else {
              const uuid = value.split(".").slice(1).join(".");
              const item = fromUuidSync(uuid);
              if (item)
                labelDetail = item.name;
              else
                labelDetail = uuid;
            }
          } else if (value.startsWith("function")) {
            icon = CONFIG.Macro.sidebarIcon;
            labelDetail = value.split(".").slice(-1).join(".");
          } else if (value.startsWith("Macro")) {
            icon = CONFIG.Macro.sidebarIcon;
            labelDetail = value.split(".").slice(1).join(".");
          } else labelDetail = value;
          obj[foundry.utils.randomID()] = {
            icon: `<i class="${icon}"></i>`,
            //          label: (flagData.label ?? "Bonus") + `  (${foundry.utils.getProperty(flagData, allSelector) ?? "0"})`,
            label: (flagData?.label ?? "Bonus") + (debugEnabled > 0 ? `: ${labelDetail}` : ""),
            value,
            key: flag,
            callback: this.data.callback
          };
        }
      }
      return obj;
    }, {});
    this.data.buttons.no = {
      icon: '<i class="fas fa-xmark"></i>',
      label: i18n("Cancel"),
      callback: /* @__PURE__ */ __name(() => {
        this.data.flags = [];
        this.close();
      }, "callback")
    };
    return {
      ...context,
      content: this.data.content,
      // This is set by the callback
      buttons: this.data.buttons
    };
  }
  set1SecondTimeout() {
    this.secondTimeoutId = setTimeout(() => {
      clearTimeout(this.secondTimeoutId);
      if (!this.timeoutId) return;
      this.timeRemaining -= 1;
      let color = "red";
      if (this.timeRemaining >= this.data.timeout * 0.75) color = "chartreuse";
      else if (this.timeRemaining >= this.data.timeout * 0.5) color = "yellow";
      else if (this.timeRemaining >= this.data.timeout * 0.25) color = "orange";
      this._updateFrame({ window: { title: this.title } });
      const title = this.element?.querySelector(".window-title");
      if (title) title.setAttribute("style", `color: ${color}`);
      if (this.timeRemaining > 0 && title) this.set1SecondTimeout();
    }, 1e3);
  }
  _onDiceRollClick(event) {
    event.preventDefault();
    let roll = event.currentTarget;
    this.rollExpanded = !this.rollExpanded;
    roll?.querySelectorAll(".dice-tooltip")?.forEach((tip) => {
      tip.classList.toggle("expanded", this.rollExpanded);
    });
  }
  _onClickButton(event) {
    if (this.secondTimeoutId) {
      clearTimeout(this.secondTimeoutId);
      this.secondTimeoutId = void 0;
    }
    const id = event.currentTarget.dataset?.button;
    const button = this.data.buttons[id];
    this.submit(button);
  }
  // _onKeyDown(event: KeyboardEvent) {
  //   // Close dialog
  //   if (event.key === "Escape" || event.key === "Enter") {
  //     event.preventDefault();
  //     event.stopPropagation();
  //     this.close();
  //   }
  // }
  async submit(button) {
    if (this.secondTimeoutId) {
      clearTimeout(this.secondTimeoutId);
    }
    try {
      if (button.callback) {
        await button.callback(this, button);
        if (this.secondTimeoutId) {
          clearTimeout(this.secondTimeoutId);
          this.secondTimeoutId = void 0;
        }
      }
    } catch (err) {
      const message = "midi-qol | Optional flag roll error see console for details ";
      ui.notifications?.error(message);
      TroubleShooter.recordError(err, message);
      error2(err);
    }
  }
  async close() {
    if (this.timeoutId) clearTimeout(this.timeoutId);
    this.timeoutId = void 0;
    if (this.secondTimeoutId) clearTimeout(this.secondTimeoutId);
    this.secondTimeoutId = void 0;
    if (this.data.close) this.data.close();
    $(document).off("keydown.chooseDefault");
    return super.close();
  }
};
async function displayBeforeAfterRolls(data) {
  let { originalRoll, newRoll, rollMode, title, player, options, actor } = data;
  options ??= {};
  options.messageData ??= {};
  const newRollHTML = await midiRenderRoll(newRoll);
  const originalRollHTML = await midiRenderRoll(originalRoll);
  const chatData = foundry.utils.mergeObject(options.messageData ?? {}, {
    flavor: `${title}`,
    speaker: ChatMessage.getSpeaker({ actor }),
    content: `${originalRollHTML}<br>${newRollHTML}`,
    whisper: [player?.id ?? ""],
    rolls: [originalRoll, newRoll],
    sound: CONFIG.sounds.dice
  }, { inplace: false });
  if (originalRoll.options.rollMode) ChatMessage.applyRollMode(chatData, originalRoll.options.rollMode);
  else ChatMessage.applyRollMode(chatData, rollMode);
  foundry.utils.setProperty(newRoll, `flags.${MODULE_ID}.chatMessageShown`, true);
  return await ChatMessage.create(chatData);
}
__name(displayBeforeAfterRolls, "displayBeforeAfterRolls");
async function bonusDialog(bonusFlags, flagSelector, showRoll, title, roll, rollType, options = {}) {
  const showDiceSoNice = !options.hideDSN && dice3dEnabled();
  let timeoutId;
  if (!roll) return void 0;
  let newRoll = roll;
  let originalRoll = roll;
  let rollHTML = await midiRenderRoll(roll);
  const player = playerForActor(this.actor);
  const flagOwners = options.flagOwners ?? {};
  const ownerOf = /* @__PURE__ */ __name((flagName) => flagOwners[flagName] ?? this.actor, "ownerOf");
  const callback = /* @__PURE__ */ __name(async (dialog, button) => {
    if (this.secondTimeoutId) {
      clearTimeout(this.secondTimeoutId);
    }
    let reRoll;
    let chatMessage;
    const undoId = foundry.utils.randomID();
    const undoData = {
      id: undoId,
      userId: player?.id ?? "",
      userName: player?.name ?? "Gamemaster",
      itemName: button.label,
      itemUuid: "",
      actorUuid: this.actor.uuid,
      actorName: this.actor.name,
      isReaction: true
    };
    await unTimedExecuteAsGM("queueUndoDataDirect", undoData);
    const buttonOwner = ownerOf(button.key);
    let rollMode = roll.options.rollMode;
    rollMode = foundry.utils.getProperty(buttonOwner ?? {}, button.key)?.rollMode ?? rollMode ?? safeGetGameSetting("core", "rollMode");
    if (!hasEffectGranting(this.actor, button.key, flagSelector, buttonOwner)) return;
    let resultApplied = false;
    let macroToCall = "";
    const allFlagSelector = flagSelector.split(".").slice(0, -1).join(".") + ".all";
    let specificMacro = false;
    const possibleMacro = foundry.utils.getProperty(buttonOwner ?? {}, `${button.key}.${flagSelector}`) || foundry.utils.getProperty(buttonOwner ?? {}, `${button.key}.${allFlagSelector}`);
    if (possibleMacro && typeof button.value === "string" && (button.value.trim().startsWith("ItemMacro") || button.value.trim().startsWith("Macro") || button.value.trim().startsWith("function"))) {
      macroToCall = button.value;
      if (macroToCall.startsWith("Macro.")) macroToCall = macroToCall.replace("Macro.", "");
      specificMacro = true;
    } else if (foundry.utils.getProperty(buttonOwner ?? {}, `${button.key}.macroToCall`)?.trim()) {
      macroToCall = foundry.utils.getProperty(buttonOwner ?? {}, `${button.key}.macroToCall`)?.trim();
    }
    if (macroToCall) {
      let result;
      let workflow;
      if (this instanceof Workflow) {
        workflow = this;
      } else {
        const itemUuidOrName = macroToCall.split(".").slice(1).join(".");
        let item = fromUuidSync(itemUuidOrName);
        if (!item && this.actor) item = this.actor.items.getName(itemUuidOrName);
        if (!item && this instanceof Actor) item = this.items.getName(itemUuidOrName);
        workflow = new DummyWorkflow(this.actor ?? this, {}, ChatMessage.getSpeaker({ actor: this.actor }), [], { item });
      }
      const macroData = workflow.getMacroData();
      macroData.macroPass = `${button.key}.${flagSelector}`;
      macroData.workflow = workflow;
      macroData.tag = "optional";
      macroData.roll = roll;
      result = await workflow.callMacro(workflow?.item, macroToCall, macroData, { roll, bonus: !specificMacro ? button.value : void 0 });
      if (typeof result === "string")
        button.value = result;
      else if (typeof result === "number")
        button.value = `${result}`;
      else if (result instanceof Roll) {
        newRoll = result;
        resultApplied = true;
      }
      if (result === void 0 && debugEnabled > 0) warn(`bonusDialog | macro ${button.value} return undefined`);
    }
    if (!resultApplied) switch (button.value) {
      case "reroll":
        reRoll = await roll.reroll();
        if (showDiceSoNice) await displayDSNForRoll(reRoll, rollType, rollMode);
        newRoll = reRoll;
        break;
      case "reroll-query":
        reRoll = reRoll = await roll.reroll();
        if (showDiceSoNice) await displayDSNForRoll(reRoll, rollType, rollMode);
        const newRollHTML = await midiRenderRoll(reRoll);
        if (await DialogV25.confirm({ window: { title: "Confirm reroll" }, content: `Replace ${rollHTML} with ${newRollHTML}`, defaultYes: true }))
          newRoll = reRoll;
        else
          newRoll = roll;
        break;
      case "reroll-kh":
        reRoll = await roll.reroll();
        if (showDiceSoNice) await displayDSNForRoll(reRoll, rollType === "attackRoll" ? "attackRollD20" : rollType, rollMode);
        newRoll = reRoll;
        if (reRoll.total <= (roll.total ?? 0)) newRoll = roll;
        break;
      case "reroll-kl":
        reRoll = await roll.reroll();
        newRoll = reRoll;
        if (reRoll.total > (roll.total ?? 0)) newRoll = roll;
        if (showDiceSoNice) await displayDSNForRoll(reRoll, rollType === "attackRoll" ? "attackRollD20" : rollType, rollMode);
        break;
      case "reroll-max":
        newRoll = await roll.reroll({ maximize: true });
        if (showDiceSoNice) await displayDSNForRoll(newRoll, rollType === "attackRoll" ? "attackRollD20" : rollType, rollMode);
        break;
      case "reroll-min":
        newRoll = await roll.reroll({ minimize: true });
        if (showDiceSoNice) await displayDSNForRoll(newRoll, rollType === "attackRoll" ? "attackRollD20" : rollType, rollMode);
        break;
      case "success":
        newRoll = newRoll = await roll.clone().evaluate();
        newRoll.terms[0].results.forEach((res) => res.result = 99);
        newRoll._total = 99;
        foundry.utils.setProperty(newRoll, "options", foundry.utils.duplicate(roll.options));
        foundry.utils.setProperty(newRoll, "options.success", true);
        break;
      case "fail":
        newRoll = newRoll = await roll.clone().evaluate();
        foundry.utils.setProperty(newRoll, "options", foundry.utils.duplicate(roll.options));
        foundry.utils.setProperty(newRoll, "options.success", false);
        newRoll.terms[0].results.forEach((res) => res.result = -1);
        newRoll._total = -1;
        break;
      case "critical":
        foundry.utils.setProperty(newRoll, "options", foundry.utils.duplicate(roll.options));
        if (newRoll.options.rollType === "damage") {
          foundry.utils.setProperty(newRoll, "options.isCritical", true);
          foundry.utils.setProperty(newRoll, "options.configured", false);
          newRoll = await newRoll.reroll();
        } else {
          const term = newRoll.terms[0];
          const result = term?.results?.find((r) => r.active)?.result ?? newRoll.total;
          foundry.utils.setProperty(term, "options.critical", result);
          foundry.utils.setProperty(term, "options.criticalSuccess", result);
          foundry.utils.setProperty(newRoll, "options.criticalSuccess", result);
        }
        if (this instanceof Workflow) this.tracker.critical.force(`${button.key}.${flagSelector}`, "Optional bonus");
        break;
      default:
        if (typeof button.value === "string" && button.value.startsWith("replace ")) {
          const rollParts = button.value.split(" ");
          newRoll = new Roll(rollParts.slice(1).join(" "), { ...(this.activity ?? this.item ?? this.actor)?.getRollData() ?? {}, ...roll.data });
          newRoll = await newRoll.evaluate();
          if (showDiceSoNice) await displayDSNForRoll(newRoll, rollType, rollMode);
        } else if (typeof button.value == "string" && button.value.startsWith("reroll-withBonus")) {
          let bonus = button.value.split("reroll-withBonus ").slice(1).join(" ");
          if (!bonus.startsWith("+")) bonus = "+" + bonus;
          newRoll = await new roll.constructor(`${roll.formula} ${bonus}`, { ...(this.activity ?? this.item ?? this.actor)?.getRollData() ?? {}, ...roll.data }).roll();
          if (showDiceSoNice) await displayDSNForRoll(newRoll, rollType, rollMode);
        } else if (flagSelector.startsWith("damage.")) {
          const DamageRoll = CONFIG.Dice.DamageRoll;
          let rollOptions = foundry.utils.duplicate(roll.options);
          if (foundry.utils.getProperty(buttonOwner ?? this.actor ?? this, `${button.key}.criticalDamage`))
            rollOptions.configured = false;
          newRoll = CONFIG.Dice.D20Roll.fromRoll(roll);
          const tempRoll = new DamageRoll(`${button.value}`, { ...(this.activity ?? this.item ?? this.actor)?.getRollData() ?? {}, ...roll.data }, rollOptions);
          await tempRoll.evaluate();
          setRollOperatorEvaluated(tempRoll);
          if (showDiceSoNice) await displayDSNForRoll(tempRoll, rollType, rollMode);
          newRoll = addRollTo(roll, tempRoll);
        } else if (flagSelector === "ac") {
          let rollOptions = foundry.utils.duplicate(roll.options);
          rollOptions.configured = false;
          newRoll = CONFIG.Dice.D20Roll.fromRoll(roll);
          const tempRoll = new Roll(`${button.value}`, { ...(this.activity ?? this.item ?? this.actor)?.getRollData() ?? {}, ...roll.data }, rollOptions);
          await tempRoll.evaluate();
          setRollOperatorEvaluated(tempRoll);
          if (showDiceSoNice) await displayDSNForRoll(tempRoll, rollType, rollMode);
          newRoll = addRollTo(roll, tempRoll);
        } else {
          newRoll = CONFIG.Dice.D20Roll.fromRoll(roll);
          const tempRoll = await new Roll(`${button.value}`, { ...(this.activity ?? this.item ?? this.actor)?.getRollData() ?? {}, ...roll.data }).roll();
          if (showDiceSoNice) await displayDSNForRoll(tempRoll, rollType, rollMode);
          newRoll = addRollTo(newRoll, tempRoll);
        }
        break;
    }
    if (showRoll && this.category === "ac") {
      const newRollHTML = await midiRenderRoll(newRoll);
      const chatData = {
        flavor: i18n("DND5E.ArmorClass"),
        content: `${newRollHTML}`,
        whisper: [player?.id ?? ""]
      };
      ChatMessage.applyRollMode(chatData, rollMode);
      chatMessage = await ChatMessage.create(chatData);
    }
    await removeEffectGranting(this.actor, button.key);
    roll = newRoll;
    const optionalsUsed = foundry.utils.getProperty(roll, `flags.${MODULE_ID}.optionalsUsed`) ?? [];
    optionalsUsed.push(`${button.key}.${flagSelector}`);
    foundry.utils.setProperty(roll, `flags.${MODULE_ID}.optionalsUsed`, optionalsUsed);
    if (dialog) {
      validFlags = validFlags.filter((bf) => bf !== button.key);
      if (validFlags.length === 0) {
        dialog?.close();
        return;
      }
      const newRollHTML = (
        /*reRoll ? await midiRenderRoll(reRoll) :*/
        await midiRenderRoll(newRoll)
      );
      dialog.data.flags = validFlags;
      dialog.data.currentRoll = newRoll;
      if (game.user?.isGM) {
        dialog.data.content = newRollHTML;
      } else {
        if (["publicroll", "gmroll", "selfroll"].includes(rollMode)) dialog.data.content = newRollHTML;
        else dialog.data.content = "Hidden Roll";
      }
      dialog.render(true);
    }
    if (chatMessage) unTimedExecuteAsGM("updateUndoChatCardUuidsById", { id: undoId, chatCardUuids: [(await chatMessage).uuid] });
  }, "callback");
  let parameters = {};
  if (!(this instanceof Workflow) && this.optionalBonusEffectsAC) {
    let sourceToken = fromUuidSync(this.triggerTokenUuid)?.object;
    const sourceActor = sourceToken?.actor ?? fromUuidSync(this.optionalBonusEffectsAC.workflowOptions?.sourceActorUuid);
    if (!sourceToken && sourceActor) sourceToken = getOrCreateTokenForActor(sourceActor);
    parameters = {
      ...this.optionalBonusEffectsAC,
      actor: sourceActor,
      tokenUuid: sourceToken?.document?.uuid,
      target: fromUuidSync(this.tokenUuid),
      triggeringRoll: this.roll,
      triggeringRollTotal: this.rollTotal,
      triggeringRollHTML: this.rollHTML,
      options
    };
  } else {
    parameters = {
      activity: this.activity,
      item: this.item,
      actor: this.actor,
      target: this.targets?.first(),
      options
    };
  }
  ;
  const conditionData = createConditionData({ workflow: this instanceof Workflow ? this : void 0, ...parameters });
  let validFlags = [];
  let lastForceFlag = "";
  const oldRoll = foundry.utils.deepClone(roll);
  for (let flagName of bonusFlags) {
    const owner = ownerOf(flagName);
    if (getOptionalCountRemaining(this.actor, `${flagName}.count`, owner) < 1) continue;
    let activationCondition = foundry.utils.getProperty(owner ?? {}, `${flagName}.activation`);
    if (activationCondition !== void 0) {
      activationCondition = await evalCondition(activationCondition, conditionData, { errorReturn: true, async: true });
      if (!activationCondition) continue;
    }
    let forcedCondition = foundry.utils.getProperty(owner ?? {}, `${flagName}.force`);
    if (forcedCondition !== void 0) {
      forcedCondition = await evalCondition(forcedCondition, conditionData, { errorReturn: true, async: true });
      if (forcedCondition) {
        const altFlag = flagSelector.split(".").slice(0, -1).join(".") + ".all";
        await callback(void 0, {
          key: flagName,
          value: foundry.utils.getProperty(owner ?? {}, `${flagName}.${flagSelector}`) ?? foundry.utils.getProperty(owner ?? {}, `${flagName}.${altFlag}`) ?? "",
          label: "none"
        });
        lastForceFlag = flagName;
      }
      continue;
    }
    if (foundry.utils.getProperty(owner, flagName) !== void 0) validFlags.push(flagName);
  }
  if (showRoll && lastForceFlag !== "") {
    DSNMarkDiceDisplayed(roll);
    const rollMode = foundry.utils.getProperty(ownerOf(lastForceFlag) ?? {}, lastForceFlag)?.rollMode ?? oldRoll.options.rollMode ?? safeGetGameSetting("core", "rollMode");
    const card = await displayBeforeAfterRolls({ originalRoll: oldRoll, newRoll: roll, rollMode, title, player, options, actor: this.actor });
    if (card?.uuid && this instanceof Workflow) {
      await unTimedExecuteAsGM("updateUndoChatCardUuidsById", { id: this.undoId, chatCardUuids: [card.uuid] });
    }
  }
  if (validFlags.length === 0) return roll;
  let timeout = options.timeout ?? configSettings.reactionTimeout ?? defaultTimeout;
  return new Promise((resolve, reject) => {
    async function onClose() {
      if (timeoutId) clearTimeout(timeoutId);
      DSNMarkDiceDisplayed(originalRoll);
      DSNMarkDiceDisplayed(newRoll);
      if (showRoll && newRoll !== originalRoll) {
        const card = await displayBeforeAfterRolls({ originalRoll, newRoll, rollMode: originalRoll.options.rollMode, title, player, options, actor: this.actor });
        if (card?.uuid && this instanceof Workflow) {
          await unTimedExecuteAsGM("updateUndoChatCardUuidsById", { id: this.undoId, chatCardUuids: [card.uuid] });
        }
      }
      resolve(newRoll);
    }
    __name(onClose, "onClose");
    if (options.timeout) {
      timeoutId = setTimeout(() => {
        resolve(newRoll);
      }, timeout * 1e3);
    }
    let content;
    let rollMode = options?.rollMode ?? safeGetGameSetting("core", "rollMode");
    if (game.user?.isGM) {
      content = rollHTML;
    } else {
      if (["publicroll", "gmroll", "selfroll"].includes(rollMode)) content = rollHTML;
      else content = "Hidden Roll";
    }
    const dialog = new RollModifyDialog(
      {
        actor: this.actor,
        flags: validFlags,
        flagSelector,
        targetObject: this,
        title,
        content,
        currentRoll: roll,
        rollHTML,
        rollMode: rollType,
        callback,
        close: onClose.bind(this),
        timeout,
        item: this.item,
        workflow: this instanceof Workflow ? this : void 0,
        flagOwners
      }
    ).render({ force: true });
  });
}
__name(bonusDialog, "bonusDialog");
function getOptionalCountRemainingShortFlag(actor, flag, owner) {
  const flagPrefix = `flags.${MODULE_ID}.optional.${flag}`;
  const countRemaining = getOptionalCountRemaining(actor, `${flagPrefix}.count`, owner) && getOptionalCountRemaining(actor, `${flagPrefix}.countAlt`, owner);
  return countRemaining;
}
__name(getOptionalCountRemainingShortFlag, "getOptionalCountRemainingShortFlag");
function getOptionalItemUsesItemMatch(actor, countValue, returnItem = false) {
  let itemNames = countValue.split(".");
  let itemName;
  let item;
  if (itemNames[1] === "identifier") {
    itemName = itemNames[2];
    item = actor.items.find((i) => i.identifier === itemName);
  } else if (itemNames[1] === "partialNameMatch") {
    itemName = itemNames[2];
    item = actor.items.find((i) => i.name.includes(itemName));
  } else if (itemNames[1] === "exactNameMatch") {
    itemName = itemNames[2];
    item = actor.items.getName(itemName);
  } else {
    itemName = itemNames[1];
    item = actor.items.getName(itemName);
  }
  if (returnItem) {
    if (!item) {
      const message = `midi-qol | removeEffectGranting | could not decrement uses for ${itemName} on actor ${actor.name}`;
      error2(message);
      TroubleShooter.recordError(new Error(message), message);
      return void 0;
    } else {
      return item;
    }
  } else {
    return item?.system.uses.value;
  }
}
__name(getOptionalItemUsesItemMatch, "getOptionalItemUsesItemMatch");
function getOptionalActivityUsesActivityMatch(actor, countValue, returnActivity = false) {
  let activityNames = countValue.split(".");
  let itemName;
  let activityName;
  let item;
  let activity;
  if (activityNames[1] === "identifier") {
    itemName = activityNames[2];
    activityName = activityNames[3];
    item = actor.items.find((i) => i.identifier === itemName);
    activity = item?.system.activities?.contents?.find((a) => a.identifier === activityName);
  } else if (activityNames[1] === "partialNameMatch") {
    itemName = activityNames[2];
    activityName = activityNames[3];
    item = actor.items.find((i) => i.name.includes(itemName));
    activity = item?.system.activities?.contents?.find((a) => a.name.includes(activityName));
  } else if (activityNames[1] === "id") {
    itemName = activityNames[2];
    activityName = activityNames[3];
    activity = actor.items.get(itemName)?.system.activities?.get(activityName);
  } else if (activityNames[1] === "exactNameMatch") {
    itemName = activityNames[2];
    activityName = activityNames[3];
    item = actor.items.getName(itemName);
    activity = item?.system.activities?.getName(activityName);
  } else {
    itemName = activityNames[1];
    activityName = activityNames[2];
    item = actor.items.getName(itemName);
    activity = item?.system.activities?.getName(activityName);
  }
  if (returnActivity) {
    if (!activity) {
      const message = `midi-qol | removeEffectGranting | could not decrement uses for ${itemName}'s activity ${activityName} on actor ${actor.name}`;
      error2(message);
      TroubleShooter.recordError(new Error(message), message);
      return void 0;
    } else {
      return activity;
    }
  } else {
    return activity?.uses.value;
  }
}
__name(getOptionalActivityUsesActivityMatch, "getOptionalActivityUsesActivityMatch");
function getOptionalCountRemaining(actor, flag, owner) {
  const countValue = foundry.utils.getProperty(owner ?? actor, flag);
  if (!countValue) return 1;
  if (Number.isNumeric(countValue)) return countValue;
  if (["each-round", "each-turn"].includes(countValue) && game.combat) {
    let usedFlag = flag.replace(".count", ".used");
    if (foundry.utils.getProperty(actor, usedFlag) === game.combat.uuid) return 0;
  } else if (["turn"].includes(countValue) && game.combat) {
    let usedFlag = flag.replace(".count", ".used");
    if (foundry.utils.getProperty(actor, usedFlag) === game.combat.uuid || game.combat?.turns[game.combat?.turn ?? ""]?.actor?.uuid !== actor?.uuid) return 0;
  } else if (countValue === "reaction") {
    return actor.flags?.[MODULE_ID]?.actions?.reactionCombatRound && needsReactionCheck(actor) ? 0 : 1;
  } else if (countValue === "bonusAction") {
    return actor.flags?.[MODULE_ID]?.actions?.bonusActionCombatRound && needsBonusActionCheck(actor) ? 0 : 1;
  } else if (countValue === "every") return 1;
  else if (countValue.startsWith("ActivityUses.")) return getOptionalActivityUsesActivityMatch(actor, countValue, false);
  else if (countValue.startsWith("ItemUses.")) return getOptionalItemUsesItemMatch(actor, countValue, false);
  if (countValue.startsWith("@")) {
    let result = foundry.utils.getProperty(actor?.system ?? {}, countValue.slice(1));
    return result;
  }
  return 1;
}
__name(getOptionalCountRemaining, "getOptionalCountRemaining");
async function removeEffectGranting(actor, changeKey) {
  const effect = actor.appliedEffects.find((ef) => ef.changes.some((c) => c.key.includes(changeKey)));
  if (effect === void 0) return;
  const effectData = effect.toObject();
  const count = effectData.changes.find((c) => c.key.includes(changeKey) && c.key.endsWith(".count"));
  const countAlt = effectData.changes.find((c) => c.key.includes(changeKey) && c.key.endsWith(".countAlt"));
  if (!count) {
    return expireEffects(actor, [effect], { "expiry-reason": "midi-qol:optionalConsumed" });
  }
  if (Number.isNumeric(count.value) || Number.isNumeric(countAlt?.value)) {
    if (Number(count.value) <= 1 || Number(countAlt?.value) <= 1)
      return expireEffects(actor, [effect], { "expiry-reason": "midi-qol:optionalConsumed" });
    else if (Number.isNumeric(count.value)) {
      count.value = `${Number(count.value) - 1}`;
    } else if (Number.isNumeric(countAlt?.value)) {
      countAlt.value = `${Number(countAlt.value) - 1}`;
    }
    await effect.update({ changes: effectData.changes });
  }
  if (typeof count.value === "string" && count.value.startsWith("ItemUses.")) {
    const item = getOptionalItemUsesItemMatch(actor, count.value, true);
    if (!item) return;
    await item.update({ "system.uses.spent": Math.max(0, item.system.uses.spent + 1) });
  }
  if (typeof countAlt?.value === "string" && countAlt.value.startsWith("ItemUses.")) {
    const item = getOptionalItemUsesItemMatch(actor, countAlt.value, true);
    if (!item) return;
    await item.update({ "system.uses.spent": Math.max(0, item.system.uses.spent + 1) });
  }
  if (typeof count.value === "string" && count.value.startsWith("ActivityUses.")) {
    const activity = getOptionalActivityUsesActivityMatch(actor, count.value, true);
    if (!activity) return;
    await activity.update({ "uses.spent": Math.max(0, activity.uses.spent + 1) });
  }
  if (typeof countAlt?.value === "string" && countAlt.value.startsWith("ActivityUses.")) {
    const activity = getOptionalActivityUsesActivityMatch(actor, countAlt.value, true);
    if (!activity) return;
    await activity.update({ "uses.spent": Math.max(0, activity.uses.spent + 1) });
  }
  const actorUpdates = {};
  if (typeof count.value === "string" && count.value.startsWith("@")) {
    let key = count.value.slice(1);
    if (key.startsWith("system.")) key = key.replace("system.", "");
    let charges = foundry.utils.getProperty(actor?.system ?? {}, key);
    if (charges) {
      charges -= 1;
      actorUpdates[`system.${key}`] = charges;
    }
  }
  if (typeof countAlt?.value === "string" && countAlt.value.startsWith("@")) {
    let key = countAlt.value.slice(1);
    if (key.startsWith("system.")) key = key.replace("system.", "");
    let charges = foundry.utils.getProperty(actor?.system ?? {}, key);
    if (charges) {
      charges -= 1;
      actorUpdates[`system.${key}`] = charges;
    }
  }
  if (["turn", "each-round", "each-turn"].includes(count.value)) {
    const flagKey = `${changeKey}.used`.replace(`flags.${MODULE_ID}.`, "");
    actorUpdates[`${changeKey}.used`] = game.combat?.uuid;
  }
  if (["turn", "each-round", "each-turn"].includes(countAlt?.value ?? "")) {
    const flagKey = `${changeKey}.used`.replace(`flags.${MODULE_ID}.`, "");
    actorUpdates[`${changeKey}.used`] = game.combat?.uuid;
  }
  if (!foundry.utils.isEmpty(actorUpdates)) await actor.update(actorUpdates);
  if (count.value === "reaction" || countAlt?.value === "reaction") {
    await setReactionUsed(actor);
  }
  if (count.value === "bonusAction" || countAlt?.value === "bonusAction") {
    await setBonusActionUsed(actor);
  }
}
__name(removeEffectGranting, "removeEffectGranting");
function hasEffectGranting(actor, key, selector, owner) {
  if (getOptionalCountRemainingShortFlag(actor, key, owner) <= 0) return false;
  const source = owner ?? actor;
  let changeKey = `${key}.${selector}`;
  let hasKey = foundry.utils.getProperty(source ?? {}, changeKey);
  if (hasKey !== void 0) return true;
  let allKey = selector.split(".");
  allKey[allKey.length - 1] = "all";
  changeKey = `${key}.${allKey.join(".")}`;
  hasKey = foundry.utils.getProperty(source ?? {}, changeKey);
  if (hasKey !== void 0) return hasKey;
  return false;
}
__name(hasEffectGranting, "hasEffectGranting");
function maxCastLevel(actor) {
  if (configSettings.ignoreSpellReactionRestriction) return 9;
  const spells = actor.system.spells;
  if (!spells) return 0;
  let pactLevel = spells.pact?.value ? spells.pact?.level : 0;
  for (let i = 9; i > pactLevel; i--) {
    if (spells[`spell${i}`]?.value > 0) return i;
  }
  return pactLevel;
}
__name(maxCastLevel, "maxCastLevel");
var reactionTypes = {
  "reaction": { prompt: "midi-qol.reactionFlavorHit", triggerLabel: "isHit" },
  "reactiontargeted": { prompt: "midi-qol.reactionFlavorTargeted", triggerLabel: "isTargeted" },
  "reactionhit": { prompt: "midi-qol.reactionFlavorHit", triggerLabel: "isHit" },
  "reactionmissed": { prompt: "midi-qol.reactionFlavorMiss", triggerLabel: "isMissed" },
  "reactioncritical": { prompt: "midi-qol.reactionFlavorCrit", triggerLabel: "isCrit" },
  "reactionfumble": { prompt: "midi-qol.reactionFlavorFumble", triggerLabel: "isFumble" },
  "reactionheal": { prompt: "midi-qol.reactionFlavorHeal", triggerLabel: "isHealed" },
  "reactiondamage": { prompt: "midi-qol.reactionFlavorDamage", triggerLabel: "isDamaged" },
  "reactionpreattack": { prompt: "midi-qol.reactionFlavorPreAttack", triggerLabel: "preAttack" },
  "reactionattacked": { prompt: "midi-qol.reactionFlavorAttacked", triggerLabel: "isAttacked" },
  "reactionsave": { prompt: "midi-qol.reactionFlavorSave", triggerLabel: "isSave" },
  "reactionsavefail": { prompt: "midi-qol.reactionFlavorSaveFail", triggerLabel: "isSaveFail" },
  "reactionsavesuccess": { prompt: "midi-qol.reactionFlavorSaveSuccess", triggerLabel: "isSaveSuccess" },
  "reactionmoved": { prompt: "midi-qol.reactionFlavorMoved", triggerLabel: "isMoved" }
};
function reactionPromptFor(triggerType) {
  if (reactionTypes[triggerType]) return reactionTypes[triggerType].prompt;
  return "midi-qol.reactionFlavorAttack";
}
__name(reactionPromptFor, "reactionPromptFor");
function reactionTriggerLabelFor(triggerType) {
  if (reactionTypes[triggerType]) return reactionTypes[triggerType].triggerLabel;
  return "reactionHit";
}
__name(reactionTriggerLabelFor, "reactionTriggerLabelFor");
async function doReactions(targetRef, triggerTokenUuid, triggeringRoll, triggerType, options = {}) {
  const target = getToken(targetRef);
  try {
    const noResult = { name: void 0, uuid: void 0, ac: void 0 };
    if (!target) return noResult;
    if (!target.actor || !target.actor.flags) return noResult;
    if (checkIncapacitated(target.actor, debugEnabled > 0, false)) return noResult;
    let player = playerFor(getTokenDocument(target));
    const usedReaction = hasUsedReaction(target.actor);
    if (getReactionSetting(player) === "none") return noResult;
    if (!player || !player.active) player = ChatMessage.getWhisperRecipients("GM").find((u) => u.active);
    if (!player) return noResult;
    const maxLevel = maxCastLevel(target.actor);
    let reactions = [];
    let reactionCount = 0;
    let reactionActivityList = [];
    try {
      const items = target.actor.items;
      for (let item of items) {
        const theItem = item instanceof Item ? item : item.baseItem;
        if (!theItem.system.activities) continue;
        if (!!item.getFlag("dnd5e", "cachedFor")) continue;
        for (let activity of theItem.system.activities) {
          const activationType = activity.effectiveActivationType ?? activity.activation?.type;
          if (!activationType?.includes("reaction")) continue;
          if (activationType !== "reaction") {
            console.warn(`midi-qol | itemReaction | item ${item.name} ${activity.name} has a reaction type of ${activationType} which is deprecated - please update to reaction and reaction conditions`);
          }
          if ((activity.activation?.value ?? 1) > 0 && usedReaction) continue;
          if (!item.system.attuned && item.system.attunement === "required") continue;
          let reactionCondition = activity.reactionCondition;
          if (activity.type === "cast" && activity.cachedSpell) {
            const spell = activity.cachedSpell;
            const spellActivity = spell.system?.activities?.filter((a) => a.canUse)?.[0];
            const spellCondition = spellActivity?.reactionCondition || "";
            if (reactionCondition && spellCondition) {
              reactionCondition = `(${reactionCondition}) && (${spellCondition})`;
            } else {
              reactionCondition = reactionCondition || spellCondition;
            }
          }
          if (item.type === "spell") {
            if (configSettings.ignoreSpellReactionRestriction) {
              reactions.push(activity);
              continue;
            }
            if (item.system.prepared === 0 && item.system.method === "spell") continue;
            if (item.system.level > 0 && !["atwill", "innate"].includes(item.system.method) && item.system.level > maxLevel) continue;
          }
          const config = activity._prepareUsageConfig({ create: false });
          if (item.type === "spell") foundry.utils.setProperty(config, "consume.spellSlot", false);
          const canUse = await activity._prepareUsageUpdates(config, { returnErrors: true });
          if (canUse instanceof Array) continue;
          if (!reactionCondition) {
            reactionCondition = 'reaction === "isHit"';
          }
          const returnvalue = await evalReactionActivationCondition(options.workflow, reactionCondition, target, { async: true, extraData: { reaction: reactionTriggerLabelFor(triggerType) } });
          if (debugEnabled > 0) warn(`for ${target.actor.name} ${theItem.name} using condition ${reactionCondition} condition ${returnvalue}`, options.workflow?.conditionData);
          if (!returnvalue) continue;
          reactions.push(activity);
        }
      }
      ;
      if (debugEnabled > 0)
        warn(`doReactions ${triggerType} for ${target.actor.name} ${target.name}`, reactions);
      reactionActivityList = reactions.map((activity) => {
        return activity.uuid;
      });
    } catch (err) {
      const message = `midi-qol | fetching reactions`;
      error2(message);
      TroubleShooter.recordError(err, message);
    } finally {
      enableNotifications(true);
    }
    if (await asyncHooksCall2("midi-qol.ReactionFilter", reactions, options, triggerType, reactionActivityList) === false) {
      console.warn("midi-qol | Reaction processing cancelled by Hook");
      return { name: "Filter", ac: 0, uuid: void 0 };
    }
    reactionCount = reactionActivityList?.length ?? 0;
    if (!usedReaction) {
      const midiFlags2 = target.actor.flags[MODULE_ID];
      reactionCount = reactionCount + Object.keys(midiFlags2?.optional ?? []).filter((flag) => {
        if (!(triggerType === "reactionattacked" && midiFlags2?.optional?.[flag].ac)) return false;
        if (!midiFlags2?.optional[flag].count) return true;
        return getOptionalCountRemainingShortFlag(target.actor, flag) > 0;
      }).length;
    }
    if (reactionCount <= 0) return noResult;
    let chatMessage;
    const reactionFlavor = game.i18n?.format(reactionPromptFor(triggerType), { itemName: options.item?.name ?? "unknown", actorName: target.name });
    const chatData = {
      content: reactionFlavor,
      whisper: [player.id]
    };
    const workflow = options.workflow ?? Workflow.getWorkflow(options?.activity?.uuid);
    if (configSettings.showReactionChatMessage) {
      const targetDocument = target.document;
      if (configSettings.enableDDBGL && installedModules.get("ddb-game-log")) {
        if (workflow?.flagTags) chatData.flags = workflow.flagTags;
      }
      chatMessage = await ChatMessage.create(chatData);
    }
    const rollOptions = showReactionAttackRollOptions;
    let content = reactionFlavor;
    if (["isHit", "isMissed", "isCrit", "isFumble", "isAttacked"].includes(reactionTriggerLabelFor(triggerType))) {
      switch (configSettings.showReactionAttackRoll) {
        case "all":
          content = `${reactionFlavor} - ${i18n(rollOptions.all)} ${triggeringRoll?.total ?? ""}`;
          break;
        case "allCrit":
          const criticalString = triggeringRoll?.isCritical ? `<span style="color: green">(${i18n("DND5E.Critical")})</span>` : "";
          content = `${reactionFlavor} - ${i18n(rollOptions.all)} ${triggeringRoll?.total ?? ""} ${criticalString}`;
          break;
        case "d20":
          const theRoll = triggeringRoll?.terms[0]?.results ? triggeringRoll.terms[0].results[0].result : triggeringRoll?.terms[0]?.total ? triggeringRoll.terms[0].total : "";
          content = `${reactionFlavor} ${i18n(rollOptions.d20)} ${theRoll}`;
          break;
        default:
          content = reactionFlavor;
      }
    }
    let result = await new Promise((resolve) => {
      const timeoutId = setTimeout(() => {
        resolve(noResult);
      }, (configSettings.reactionTimeout ?? defaultTimeout) * 1e3 * 2);
      player && requestReactions(target, player, triggerTokenUuid, content ?? "", triggerType, reactionActivityList, resolve, chatMessage, options).then((result2) => {
        clearTimeout(timeoutId);
      });
    });
    if (result?.name) {
      let count = 100;
      do {
        await busyWait(50);
        count -= 1;
      } while (globalThis.DAE.actionQueue.remaining && count);
      target.actor.reset();
      workflow?.actor.reset();
    }
    return result;
  } catch (err) {
    const message = `doReactions error ${triggerType} for ${target?.name} ${triggerTokenUuid}`;
    TroubleShooter.recordError(err, message);
    throw err;
  }
}
__name(doReactions, "doReactions");
async function requestReactions(target, player, triggerTokenUuid, reactionFlavor, triggerType, reactionActivityList, resolve, chatPromptMessage, options = {}) {
  try {
    const startTime = Date.now();
    if (options.item && options.item instanceof CONFIG.Item.documentClass) {
      options.itemUuid = options.item.uuid;
      delete options.item;
    }
    if (options.workflow) {
      options.workflowId = options.workflow.id;
      delete options.workflow;
    }
    let result;
    if (player.isGM) {
      result = await unTimedExecuteAsGM("chooseReactions", {
        tokenUuid: target.document.uuid,
        reactionFlavor,
        triggerTokenUuid,
        triggerType,
        options,
        reactionActivityList
      });
    } else {
      result = await socketlibSocket.executeAsUser("chooseReactions", player.id, {
        tokenUuid: target.document.uuid,
        reactionFlavor,
        triggerTokenUuid,
        triggerType,
        options,
        reactionActivityList
      });
    }
    const endTime = Date.now();
    if (debugEnabled > 0) warn("requestReactions | returned after ", endTime - startTime, result);
    resolve(result);
    if (chatPromptMessage) chatPromptMessage.delete();
  } catch (err) {
    const message = `requestReactions | error ${triggerType} for ${target?.name} ${triggerTokenUuid}`;
    TroubleShooter.recordError(err, message);
    error2(message, err);
    throw err;
  }
}
__name(requestReactions, "requestReactions");
async function promptReactions(tokenUuid, reactionActivityList, triggerTokenUuid, reactionFlavor, triggerType, options = {}) {
  try {
    const startTime = Date.now();
    const target = fromUuidSync(tokenUuid);
    const actor = target?.actor;
    if (!actor) return;
    const usedReaction = hasUsedReaction(actor);
    const midiFlags2 = foundry.utils.getProperty(actor ?? {}, `flags.${MODULE_ID}`);
    let result;
    let reactionActivities = [];
    enableNotifications(false);
    try {
      enableNotifications(false);
      for (let ref of reactionActivityList) {
        if (typeof ref === "string") reactionActivities.push(await fromUuid(ref));
        else reactionActivities.push(ref);
      }
      ;
    } finally {
      enableNotifications(true);
    }
    if (reactionActivities.length > 0) {
      if (await asyncHooksCall2("midi-qol.ReactionFilter", reactionActivities, options, triggerType, reactionActivityList) === false) {
        console.warn("midi-qol | Reaction processing cancelled by Hook");
        return { name: "Filter" };
      }
      result = await reactionDialog(actor, triggerTokenUuid, reactionActivities, reactionFlavor, triggerType, options);
      const endTime2 = Date.now();
      if (debugEnabled > 0) warn("promptReactions | reaction processing returned after ", endTime2 - startTime, result);
      if (result.uuid) return result;
    }
    if (usedReaction) return { name: "None" };
    if (!midiFlags2) return { name: "None" };
    const validFlags = Object.keys(midiFlags2?.optional ?? {}).filter((flag) => {
      if (!midiFlags2.optional[flag].ac) return false;
      if (!midiFlags2.optional[flag].count) return true;
      return getOptionalCountRemainingShortFlag(actor, flag) > 0;
    }).map((flag) => `flags.${MODULE_ID}.optional.${flag}`);
    if (validFlags.length > 0 && triggerType === "reactionattacked") {
      let acRoll = await new Roll(`${actor.system.attributes.ac.value}`).roll();
      const data = {
        actor,
        tokenUuid,
        optionalBonusEffectsAC: options,
        triggerTokenUuid,
        roll: acRoll,
        rollHTML: reactionFlavor,
        rollTotal: acRoll.total
      };
      let displayBonusRolls = validFlags.reduce((acc, flag) => acc || foundry.utils.getProperty(actor ?? {}, `${flag}.displayBonusRolls`), void 0);
      if (!displayBonusRolls) displayBonusRolls = checkMechanic("displayBonusRolls");
      const newAC = await bonusDialog.bind(data)(validFlags, "ac", displayBonusRolls !== false, `${actor.name} - ${i18n("DND5E.AC")} ${actor.system.attributes.ac.value}`, acRoll, "roll");
      const endTime2 = Date.now();
      if (debugEnabled > 0) warn("promptReactions | returned via bonus dialog ", endTime2 - startTime);
      if (newAC?.total !== acRoll.total) {
        return { name: actor.name, uuid: actor.uuid, ac: newAC?.total };
      }
      return { name: "None" };
    }
    const endTime = Date.now();
    if (debugEnabled > 0) warn("promptReactions | returned no result ", endTime - startTime);
    return { name: "None" };
  } catch (err) {
    const message = `promptReactions ${tokenUuid} ${triggerType} ${reactionActivityList}`;
    TroubleShooter.recordError(err, message);
    throw err;
  }
}
__name(promptReactions, "promptReactions");
function playerFor(target) {
  return playerForActor(target?.actor);
}
__name(playerFor, "playerFor");
function playerForActor(actor) {
  if (!actor) return void 0;
  let user;
  const OWNERSHIP_LEVELS = CONST.DOCUMENT_OWNERSHIP_LEVELS;
  const ownwership = actor.ownership;
  if (actor.hasPlayerOwner) user = game.users?.find((u) => u.character?.id === actor?.id && u.active);
  if (!user)
    user = game.users?.players.find((p) => p.active && ownwership[p.id ?? ""] === OWNERSHIP_LEVELS.OWNER);
  if (!user)
    user = game.users?.players.find((p) => p.character?.id === actor?.id);
  if (!user)
    user = game.users?.players.find((p) => ownwership[p.id ?? ""] === OWNERSHIP_LEVELS.OWNER);
  if (!user && ownwership.default === OWNERSHIP_LEVELS.OWNER) {
    user = game.users?.players.find((p) => p.active && ownwership[p.id] === OWNERSHIP_LEVELS.INHERIT);
  }
  if (!user) user = preferredActiveGM() ?? void 0;
  return user;
}
__name(playerForActor, "playerForActor");
async function reactionDialog(actor, triggerTokenUuid, reactionActivities, rollFlavor, triggerType, options = {}) {
  const noResult = { name: "None" };
  try {
    let timeout = options.timeout ?? configSettings.reactionTimeout ?? defaultTimeout;
    return new Promise((resolve, reject) => {
      let timeoutId = setTimeout(() => {
        dialog.close();
        resolve({});
      }, timeout * 1e3);
      const callback = /* @__PURE__ */ __name(async function(dialog2, button) {
        clearTimeout(timeoutId);
        const activity = reactionActivities.find((i) => i.uuid === button.key);
        if (activity) {
          dialog2.close();
          let effectiveTargetType = activity.target?.affects?.type;
          if (activity.type === "cast" && !activity._source?.target?.override) {
            const spell = activity.cachedSpell ?? fromUuidSync(activity.spell?.uuid);
            if (spell) {
              const activities = spell.system?.activities;
              const activityList = activities instanceof Collection ? activities.contents : Object.values(activities ?? {});
              const spellActivity = activityList.find((a) => a.canUse) ?? activityList[0];
              if (spellActivity?.target?.affects?.type) effectiveTargetType = spellActivity.target.affects.type;
            }
          }
          const itemRollOptions = foundry.utils.mergeObject({
            createWorkflow: true,
            configureDialog: true,
            checkGMStatus: false,
            targetUuids: effectiveTargetType !== "self" ? [triggerTokenUuid] : [getOrCreateTokenForActor(actor)?.document?.uuid],
            isReaction: true,
            workflowOptions: { targetConfirmation: "none" },
            ignoreUserTargets: true
          }, options);
          let useTimeoutId = setTimeout(() => {
            clearTimeout(useTimeoutId);
            resolve({});
          }, (timeout - 1) * 1e3);
          let result = noResult;
          clearTimeout(useTimeoutId);
          if (activity.item instanceof CONFIG.Item.documentClass) {
            const config = { midiOptions: itemRollOptions };
            result = await completeActivityUse2(activity, config, {}, { systemCard: false });
            const workflow = result;
            if (workflow && workflow.currentAction !== workflow.WorkflowState_Cleanup) resolve(noResult);
            else if (workflow) resolve({ name: workflow.activity.name, uuid: workflow.activity.uuid, itemName: workflow.activity.item.name, itemUuid: activity.item.uuid });
            else resolve(noResult);
          }
        }
        resolve(noResult);
      }, "callback");
      const noReaction = /* @__PURE__ */ __name(async function(dialog2, button) {
        clearTimeout(timeoutId);
        resolve(noResult);
      }, "noReaction");
      const dialog = new ReactionDialog({
        actor,
        targetObject: this,
        title: `${actor.name}`,
        activities: reactionActivities,
        content: rollFlavor,
        callback,
        close: noReaction,
        timeout
      });
      dialog.render({ force: true });
    });
  } catch (err) {
    const message = `reaactionDialog error ${actor?.name} ${actor?.uuid} ${triggerTokenUuid}`;
    TroubleShooter.recordError(err, message);
    throw err;
  }
}
__name(reactionDialog, "reactionDialog");
var ReactionDialog = class extends HandlebarsApplicationMixin12(ApplicationV211) {
  static {
    __name(this, "ReactionDialog");
  }
  startTime;
  endTime;
  timeoutId;
  timeRemaining;
  data;
  constructor(data) {
    super(data);
    this.data = data;
    this.timeRemaining = this.data.timeout;
    this.startTime = this.endTime = Date.now();
    this.data.completed = false;
    this.timeoutId = this.set1Secondtimeout();
  }
  static PARTS = {
    dialog: {
      id: "dialog-reaction",
      classes: ["dialog", "midi-qol", "reaction"],
      template: "modules/midi-qol/templates/dialog.hbs"
    }
  };
  static DEFAULT_OPTIONS = {
    position: {
      width: 400,
      height: "auto"
    }
  };
  get title() {
    let maxPad = 45;
    if (this.data.timeout) {
      if (this.data.timeout < maxPad) maxPad = this.data.timeout;
      const padCount = Math.ceil(this.timeRemaining / (this.data.timeout ?? defaultTimeout) * maxPad);
      const pad = "-".repeat(padCount);
      return `${this.data.actor?.name} ${this.data.title ?? "Dialog"} ${pad} ${this.timeRemaining}`;
    } else return this.data.title ?? "Dialog";
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    for (const button of Array.from(this.element.querySelectorAll(".dialog-button"))) {
      button.addEventListener("click", this._onClickButton.bind(this));
    }
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    this.data.buttons = this.data.activities.reduce((acc, activity) => {
      let name = `${activity.item.name}: ${activity.name ?? activity.actionName}`;
      if (activity.item.system.linkedActivity) {
        const linked = activity.item.system.linkedActivity;
        name = `${linked.item.name}: ${linked.name ?? linked.actionName}`;
      }
      acc[foundry.utils.randomID()] = {
        // icon: `<image src=${item.img} width="30" height="30">`,
        label: `<div style="display: flex; align-items: center; margin: 5px;"> <image src=${activity.item.img} width="40" height="40"> &nbsp ${name} </div>`,
        value: activity.name ?? activity.actionName,
        key: activity.uuid,
        callback: this.data.callback
      };
      return acc;
    }, {});
    return {
      ...context,
      content: this.data.content,
      buttons: this.data.buttons,
      timeRemaining: this.timeRemaining
    };
  }
  set1Secondtimeout() {
    this.timeoutId = setTimeout(() => {
      this.timeRemaining -= 1;
      let color = "red";
      if (this.timeRemaining >= this.data.timeout * 0.75) color = "chartreuse";
      else if (this.timeRemaining >= this.data.timeout * 0.5) color = "yellow";
      else if (this.timeRemaining >= this.data.timeout * 0.25) color = "orange";
      this._updateFrame({ window: { title: this.title } });
      const title = this.element?.querySelector(".window-title");
      if (title) title.setAttribute("style", `color: ${color}`);
      if (this.timeRemaining > 0 && title) this.set1Secondtimeout();
    }, 1e3);
  }
  _onClickButton(event) {
    const id = event.currentTarget.dataset.button;
    const button = this.data.buttons[id];
    debug("Reaction dialog button clicked", id, button, Date.now() - this.startTime);
    this.submit(button);
  }
  // _onKeyDown(event) {
  //   // Close dialog
  //   if (event.key === "Escape" || event.key === "Enter") {
  //     debug("Reaction Dialog onKeyDown esc/enter pressed", event.key, Date.now() - this.startTime);
  //     event.preventDefault();
  //     event.stopPropagation();
  //     this.data.completed = true;
  //     if (this.data.close) this.data.close({ name: "keydown", uuid: undefined });
  //     this.close();
  //   }
  // }
  async submit(button) {
    try {
      clearTimeout(this.timeoutId);
      debug("ReactionDialog submit", Date.now() - this.startTime, button.callback);
      if (button.callback) {
        this.data.completed = true;
        await button.callback(this, button);
        this.close();
      }
    } catch (err) {
      const message = `Reaction dialog submit`;
      TroubleShooter.recordError(err, message);
      ui.notifications?.error(err);
      error2(err);
      this.data.completed = false;
      this.close();
    }
  }
  async close() {
    clearTimeout(this.timeoutId);
    debug("Reaction Dialog close ", Date.now() - this.startTime, this.data.completed);
    if (!this.data.completed && this.data.close) {
      this.data.close({ name: "Close", uuid: void 0 });
    }
    $(document).off("keydown.chooseDefault");
    return super.close();
  }
};
function getConcentrationEffect(actor, itemRef) {
  let item;
  if (!actor) return;
  if (typeof itemRef === "string") {
    item = fromUuidSync(itemRef);
    if (!item) item = actor.concentration.items?.find((i) => i.name === itemRef);
  } else item = itemRef;
  if (!item?.id)
    return actor?.effects.find((ef) => ef.statuses.has(systemConcentrationId));
  else {
    return actor?.effects.find(
      (ef) => ef.statuses.has(systemConcentrationId) && ef.flags?.dnd5e?.item?.id === item.id
    );
  }
}
__name(getConcentrationEffect, "getConcentrationEffect");
async function confirm(title = "Are you sure", { content, defaultYes } = { content: "", defaultYes: true }) {
  return DialogV25.confirm({
    window: { title: title ?? "Confirm" },
    content,
    defaultYes
  });
}
__name(confirm, "confirm");
function buildEvalSandbox(sandbox, asyncEval) {
  const extras = {
    Roll,
    findNearby,
    findNearbyCount,
    checkNearby,
    hasCondition,
    checkDefeated,
    checkIncapacitated,
    canSee,
    canSense,
    computeDistance,
    checkActivityRange,
    checkDistance,
    fromUuidSync,
    nonWorkflowTargetedToken: game.user?.targets.first()?.document.uuid,
    combat: game.combat,
    evalRaceOrType: raceOrType,
    evalTypeOrRace: typeOrRace,
    evalTypeOrRaceEval: typeOrRace,
    // legacy alias - sync eval historically only exposed this name
    sizeNumeric,
    error: console.error,
    warn: console.warn,
    log: console.log
  };
  if (asyncEval) {
    extras.contestedRoll = contestedRoll;
    extras.confirm = confirm;
  }
  return foundry.utils.mergeObject(sandbox, extras);
}
__name(buildEvalSandbox, "buildEvalSandbox");
function mySafeEvalImpl(expression, sandbox, onErrorReturn, asyncEval) {
  const label = asyncEval ? "asyncMySafeEval" : "mySafeEval";
  const coerceNumeric = /* @__PURE__ */ __name((result) => Number.isNumeric(result) ? Number(result) : result, "coerceNumeric");
  const handleError = /* @__PURE__ */ __name((err) => {
    const message = `midi-qol | ${label} | activation condition (${expression}) error, actorUuid: ${sandbox.actorUuid} itemUuid: ${sandbox.item?.uuid} targetUuid: ${sandbox.targetUuid}`;
    console.warn(message, err);
    TroubleShooter.recordError(err, message);
    return coerceNumeric(onErrorReturn);
  }, "handleError");
  try {
    if (asyncEval) {
      expression = expression.replace(/confirm\((.*)\)/g, "await confirm($1)");
    } else if (expression.includes("Roll(")) {
      error2("safeEval | Roll expressions are not supported", expression);
      expression = expression.replaceAll(/evaluate\s*\({\s*async:\s* false\s*}\)/g, "evaluateSync({strict: false})");
      error2("Expression replaced with ", expression);
    }
    const src = "with (sandbox) { return " + expression + "}";
    const evl = asyncEval ? foundry.utils.AsyncFunction("sandbox", src) : new Function("sandbox", src);
    sandbox = buildEvalSandbox(sandbox, asyncEval);
    const sandboxProxy = new Proxy(sandbox, {
      has: /* @__PURE__ */ __name(() => true, "has"),
      // Include everything
      get: /* @__PURE__ */ __name((t, k) => k === Symbol.unscopables ? void 0 : t[k] ?? Math[k], "get"),
      set: /* @__PURE__ */ __name(() => {
        error2(`midi-qol | ${label} | You may not set properties of the sandbox environment`);
        return false;
      }, "set")
      // No-op
    });
    if (MidiQOL.logEvaluationData) console.log(`MidiQOL ${label}`, { expression, sandboxProxy });
    const result = evl.call(null, sandboxProxy);
    if (asyncEval) return Promise.resolve(result).then((r) => {
      if (MidiQOL.logEvaluationData) console.log(`MidiQOL ${label}`, { result: r });
      return coerceNumeric(r);
    }, handleError);
    if (MidiQOL.logEvaluationData) console.log(`MidiQOL ${label}`, { result });
    return coerceNumeric(result);
  } catch (err) {
    return handleError(err);
  }
}
__name(mySafeEvalImpl, "mySafeEvalImpl");
async function asyncMySafeEval(expression, sandbox, onErrorReturn) {
  return mySafeEvalImpl(expression, sandbox, onErrorReturn, true);
}
__name(asyncMySafeEval, "asyncMySafeEval");
function mySafeEval(expression, sandbox, onErrorReturn) {
  return mySafeEvalImpl(expression, sandbox, onErrorReturn, false);
}
__name(mySafeEval, "mySafeEval");
async function evalReactionActivationCondition(workflow, condition, target, options = {}) {
  if (options.errorReturn === void 0) options.errorReturn = false;
  return evalActivationCondition2(workflow, condition, target, options);
}
__name(evalReactionActivationCondition, "evalReactionActivationCondition");
async function evalActivationCondition2(workflow, condition, target, options = {}) {
  if (condition === void 0 || condition === "" || condition === true) return true;
  if (condition === false) return false;
  const conditionData = createConditionData({ workflow, target, actor: workflow?.actor, extraData: options?.extraData, item: options.item });
  options.errorReturn ??= true;
  const returnValue = await evalCondition(condition, conditionData, options);
  return returnValue;
}
__name(evalActivationCondition2, "evalActivationCondition");
function typeOrRace(entity) {
  const actor = getActor(entity);
  const systemData = actor?.system;
  if (!systemData) return "";
  if (systemData.details.type?.value) return systemData.details.type?.value?.toLocaleLowerCase() ?? "";
  else return (systemData.details?.race?.name ?? systemData.details?.race)?.toLocaleLowerCase() ?? "";
}
__name(typeOrRace, "typeOrRace");
function sizeNumeric(size) {
  const s = size?.toLowerCase();
  if (!s) return void 0;
  const actorSizes = GameSystemConfig.actorSizes;
  if (actorSizes[s]?.numerical !== void 0) return actorSizes[s].numerical;
  for (const [, config] of Object.entries(actorSizes)) {
    if (game.i18n.localize(config.label).toLowerCase() === s) return config.numerical;
  }
  return void 0;
}
__name(sizeNumeric, "sizeNumeric");
function raceOrType(entity) {
  const actor = getActor(entity);
  const systemData = actor?.system;
  if (!systemData) return "";
  if (systemData.details.race) return (systemData.details?.race?.name ?? systemData.details?.race)?.toLocaleLowerCase() ?? "";
  return systemData.details.type?.value?.toLocaleLowerCase() ?? "";
}
__name(raceOrType, "raceOrType");
function collectDamageTypes(damageRolls = [], activity, rollData) {
  if (damageRolls.length) return collectDamageRollsDamageTypes(damageRolls, rollData);
  else if (activity) return collectActivityDamageTypes(activity, rollData);
  else if (rollData) {
    rollData.defaultDamageType ??= {};
    rollData.damageTypes ??= {};
    return;
  }
  return { damageTypes: {}, defaultDamageType: {} };
}
__name(collectDamageTypes, "collectDamageTypes");
function collectDamageRollsDamageTypes(damageRolls = [], rollData) {
  const existingTypes = rollData?.damageTypes ?? {};
  const damageTypes = { ...existingTypes };
  let defaultType = void 0;
  for (const roll of damageRolls) {
    const type = roll.options?.type;
    if (type && !defaultType) defaultType = type;
    if (type) damageTypes[type] = true;
    for (const part of roll.parts ?? []) {
      if (!part?.length) continue;
      const match = [...part.matchAll(/\[([^\]]+)\]/g)].map((m) => m[1].trim().toLowerCase());
      for (const partType of match) {
        if (!defaultType) defaultType = partType;
        damageTypes[partType] = true;
      }
    }
  }
  const defaultDamageType = defaultType ? { [defaultType]: true } : {};
  if (rollData) {
    rollData.damageTypes = damageTypes;
    rollData.defaultDamageType ??= defaultDamageType;
    return;
  }
  return { damageTypes, defaultDamageType };
}
__name(collectDamageRollsDamageTypes, "collectDamageRollsDamageTypes");
function collectActivityDamageTypes(activity, rollData) {
  if (!activity || !["attack", "damage", "heal", "save"].includes(activity.type)) {
    if (rollData) {
      rollData.defaultDamageType ??= {};
      rollData.damageTypes ??= {};
      return;
    }
    return { defaultDamageType: {}, damageTypes: {} };
  }
  const returnDamageTypes = {};
  let returnDefaultDamageType = void 0;
  const partTypes = /* @__PURE__ */ __name((part) => {
    if (part.types.size > 1) console.debug("MidiQOL collectActivityDamageTypes: Multiple damage types available for selection; cannot properly evaluate; damageTypes will grab the first of multiple ones");
    const type = part.types.first();
    if (type) {
      if (!returnDefaultDamageType) returnDefaultDamageType = { [type]: true };
      returnDamageTypes[type] = true;
    }
    const formula = part.custom?.formula;
    if (formula && formula !== "") {
      const match = [...formula.matchAll(/\[([^\]]+)\]/g)].map((m) => m[1].trim().toLowerCase());
      for (const m of match) {
        if (!returnDefaultDamageType) returnDefaultDamageType = { [m]: true };
        returnDamageTypes[m] = true;
      }
    }
  }, "partTypes");
  const activityType = activity.type === "heal" ? "healing" : "damage";
  if (activityType === "healing") {
    const part = activity[activityType];
  } else {
  }
  if (rollData) {
    rollData.damageTypes = returnDamageTypes;
    rollData.defaultDamageType ??= returnDefaultDamageType;
    return;
  }
  return { damageTypes: returnDamageTypes, defaultDamageType: returnDefaultDamageType ?? {} };
}
__name(collectActivityDamageTypes, "collectActivityDamageTypes");
function createConditionData(data) {
  const actor = data.workflow?.actor ?? data.actor;
  let item;
  if (data.item) {
    if (data.item instanceof Item) item = data.item;
    else if (typeof data.item === "string") item = fromUuidSync(data.item);
  }
  if (!item) item = data.activity?.item ?? data.workflow?.activity?.item ?? data.workflow?.item;
  let rollData = data.activity?.getRollData() ?? item?.getRollData() ?? actor?.getRollData() ?? {};
  const damageRolls = data.workflow?.damageRolls?.length ? data.workflow.damageRolls : data.options?.damageRolls;
  collectDamageTypes(damageRolls, data.workflow?.activity, rollData);
  rollData = foundry.utils.mergeObject(rollData, data.extraData ?? {});
  rollData.isAttuned = rollData.item?.attuned || rollData.item?.attunement === "";
  rollData.options = data?.options;
  rollData.isConcentrationCheck = foundry.utils.getProperty(rollData, "options.messageData.flags.midi-qol.isConcentrationCheck");
  rollData.isDeathSave = foundry.utils.getProperty(rollData, "options.messageData.flags.dnd5e.roll.type") === "death";
  rollData.actor = {};
  rollData.actor.raceOrType = actor ? raceOrType(actor) : "";
  rollData.actor.typeOrRace = actor ? typeOrRace(actor) : "";
  rollData.items = actor?.items?.map((i) => i.getRollData().item) ?? [];
  rollData.equippedItems = rollData.items.filter((i) => i.equipped) ?? [];
  rollData.worldTime = game.time?.worldTime;
  try {
    if (data.target) {
      const theTarget = getToken(data.target);
      if (theTarget) {
        const theTargetActor = theTarget.actor;
        rollData.target = {
          get system() {
            return theTargetActor?.system;
          },
          ...theTargetActor?.getRollData()
        };
        rollData.targetUuid = data.targetUuid ?? theTarget.document.uuid;
        rollData.targetId = theTarget.id;
        rollData.targetActorUuid = theTargetActor?.uuid;
        rollData.targetActorId = theTargetActor?.id;
        rollData.raceOrType = theTargetActor ? raceOrType(theTargetActor) : "";
        rollData.typeOrRace = theTargetActor ? typeOrRace(theTargetActor) : "";
        rollData.target.raceOrType = theTarget.actor ? raceOrType(theTargetActor) : "";
        rollData.target.typeOrRace = theTarget.actor ? typeOrRace(theTargetActor) : "";
        rollData.target.items = theTargetActor?.items?.map((i) => i.getRollData().item) ?? [];
        rollData.target.equippedItems = rollData.target.items.filter((i) => i.equipped) ?? [];
        rollData.target.saved = data.workflow?.saves.has(theTarget);
        rollData.target.failedSave = data.workflow?.failedSaves.has(theTarget);
        rollData.target.superSaver = data.workflow?.superSavers.has(theTarget);
        rollData.target.semiSuperSaver = data.workflow?.semiSuperSavers.has(theTarget);
        rollData.target.isHit = data.workflow?.hitTargets.has(theTarget);
        rollData.target.isHitEC = data.workflow?.hitTargets.has(theTarget);
        rollData.target.canSense = data.workflow?.token && data.workflow.targetsCanSense.has(data.workflow.token);
        rollData.target.canSee = data.workflow?.token && data.workflow.targetsCanSee.has(data.workflow.token);
        rollData.canSense = data.workflow?.tokenCanSense?.has(theTarget);
        rollData.canSee = data.workflow?.tokenCanSee?.has(theTarget);
        if (theTarget) rollData.target.isCombatTurn = game.combat?.combatant?.tokenId === theTarget.id;
      }
    }
    rollData.humanoid = globalThis.MidiQOL.humanoid;
    rollData.tokenUuid = data.workflow?.tokenUuid ?? data.tokenUuid;
    rollData.tokenId = data.token?.id ?? data.workflow?.token?.id;
    if (data.workflow) {
      rollData.w = data.workflow;
      rollData.workflow = data.workflow;
      rollData.activity = data.workflow.activity;
      rollData.riderStatuses = {};
      data.workflow.activity.applicableEffects?.forEach((effect) => {
        Array.from(effect?.statuses).forEach((status) => rollData.riderStatuses[status] = true);
        effect.flags?.dnd5e?.riders?.statuses?.forEach((rider) => rollData.riderStatuses[rider] = true);
      });
      rollData.otherDamageActivity = data.workflow?.otherActivity;
      rollData.hasSave = data.workflow.hasSave;
      rollData.item = data.workflow.item.getRollData().item;
      rollData.shouldRollDamage = data.workflow.shouldRollDamage;
      rollData.hasAttack = data.workflow.activity.attack;
      rollData.hasDamage = data.workflow.activity.hasDamage;
    }
    if (!data.activity && data.workflow) data.activity = data.workflow.activity;
    if (data.activity) {
      rollData.activity = data.activity;
      rollData.a = data.activity;
    }
    if (item) {
      Object.defineProperty(rollData.item, "actionType", { get() {
        return item.system.actionType;
      } });
    }
    if (data.effect) {
      rollData.effect = data.effect;
    }
    if (game.combat) {
      const combat = game.combat;
      rollData.combatRound = combat.round;
      rollData.combatTurn = combat.turn;
      rollData.combatTime = combat.round + (combat.turn ?? 0) / 100;
      rollData.actor.isCombatTurn = combat.combatant?.tokenId === data.workflow?.token?.id;
      rollData.isCombatTurn = rollData.actor.isCombatTurn;
    } else rollData.combatTime = 0;
    rollData.CONFIG = CONFIG;
    rollData.CONST = {};
    let exclusions = ["DOCUMENT_TYPES"];
    Object.keys(CONST).forEach((key) => !exclusions.includes[key] && (rollData.CONST[key] = CONST[key]));
  } catch (err) {
    const message = `midi-qol | createConditionData`;
    TroubleShooter.recordError(err, message);
    console.warn(message, err);
  } finally {
    if (data.workflow) data.workflow.conditionData = rollData;
  }
  return rollData;
}
__name(createConditionData, "createConditionData");
async function evalAllConditionsAsync(actorRef, flag, conditionData, errorReturn = false) {
  if (!flag) return errorReturn;
  let actor = getActor(actorRef);
  if (!actor) return errorReturn;
  const effects = actor.appliedEffects.filter((ef) => ef.changes.some((change) => change.key === flag));
  let keyToUse = flag.replace(`flags.${MODULE_ID}.`, "flags.midi.evaluated.");
  keyToUse = keyToUse.replace("flags.dnd5e.", "flags.midi.evaluated.dnd5e.");
  let returnValue = errorReturn;
  foundry.utils.setProperty(actor, `${keyToUse}.value`, false);
  foundry.utils.setProperty(actor, `${keyToUse}.effects`, []);
  for (let effect of effects) {
    for (let change of effect.changes) {
      if (change.key === flag) {
        const condValue = await evalCondition(change.value, conditionData, { errorReturn, async: true });
        if (debugEnabled > 0) warn("evalAllConditions Async", actor.name, flag, change.value, condValue, conditionData, errorReturn);
        if (condValue) {
          returnValue = condValue;
          foundry.utils.setProperty(actor, `${keyToUse}.value`, condValue);
          foundry.utils.getProperty(actor, `${keyToUse}.effects`).push(effect.name);
        }
      }
    }
  }
  if (effects.length === 0 && foundry.utils.getProperty(actor, flag)) {
    returnValue = await evalCondition(foundry.utils.getProperty(actor, flag), conditionData, { errorReturn, async: true });
    if (returnValue) {
      foundry.utils.setProperty(actor, `${keyToUse}.value`, returnValue);
      foundry.utils.getProperty(actor, `${keyToUse}.effects`).push("flag");
    }
  }
  return returnValue;
}
__name(evalAllConditionsAsync, "evalAllConditionsAsync");
function evalAllConditions(actorRef, flag, conditionData, errorReturn = false) {
  if (!flag) return errorReturn;
  let actor = getActor(actorRef);
  if (!actor) return errorReturn;
  const effects = actor.appliedEffects.filter((ef) => ef.changes.some((change) => change.key === flag));
  let keyToUse = flag.replace(`flags.${MODULE_ID}.`, "flags.midi.evaluated.");
  keyToUse = keyToUse.replace("flags.dnd5e.", "flags.midi.evaluated.dnd5e.");
  let returnValue = errorReturn;
  foundry.utils.setProperty(actor, `${keyToUse}.value`, false);
  foundry.utils.setProperty(actor, `${keyToUse}.effects`, []);
  for (let effect of effects) {
    for (let change of effect.changes) {
      if (change.key === flag) {
        const condValue = evalCondition(change.value, conditionData, { errorReturn, async: false });
        if (debugEnabled > 0) warn("evalAllConditions ", actor.name, flag, change.value, condValue, conditionData, errorReturn);
        if (condValue) {
          returnValue = condValue;
          foundry.utils.setProperty(actor, `${keyToUse}.value`, condValue);
          foundry.utils.getProperty(actor, `${keyToUse}.effects`).push(effect.name);
        }
      }
    }
  }
  if (effects.length === 0 && foundry.utils.getProperty(actor, flag)) {
    returnValue = evalCondition(foundry.utils.getProperty(actor, flag), conditionData, { errorReturn, async: false });
    if (returnValue) {
      foundry.utils.setProperty(actor, `${keyToUse}.value`, returnValue);
      foundry.utils.getProperty(actor, `${keyToUse}.effects`).push("flag");
    }
  }
  return returnValue;
}
__name(evalAllConditions, "evalAllConditions");
async function checkMagicResistanceAndVulnerability(actor, rollAbility2, conditionData) {
  if (!actor) {
    return { advantage: void 0, disadvantage: void 0, magicResistance: false, magicVulnerability: false };
  }
  const effectiveConditionData = conditionData ?? createConditionData({ actor, workflow: void 0, target: actor });
  let advantage = void 0;
  let disadvantage = void 0;
  let magicResistance = false;
  let magicVulnerability = false;
  const mrCustom = (i18n("midi-qol.MagicResistant") ?? "Magic Resistant").trim();
  const mvCustom = (i18n("midi-qol.MagicVulnerable") ?? "Magic Vulnerable").trim();
  if ((actor.system?.traits?.dr?.custom || "").includes(mrCustom)) {
    advantage = true;
  }
  if (actor.items?.find((a) => a.type === "feat" && a.name === i18n("midi-qol.MagicResistanceFeat")?.trim())) {
    advantage = true;
  }
  if ((actor.system?.traits?.dv?.custom || "").includes(mvCustom)) {
    disadvantage = true;
  }
  if (await evalAllConditionsAsync(actor, midiFlag2(`magicResistance.all`), effectiveConditionData) || rollAbility2 && await evalAllConditionsAsync(actor, midiFlag2(`magicResistance.${rollAbility2}`), effectiveConditionData)) {
    advantage = true;
    magicResistance = true;
  }
  if (await evalAllConditionsAsync(actor, midiFlag2(`magicVulnerability.all`), effectiveConditionData) || rollAbility2 && await evalAllConditionsAsync(actor, midiFlag2(`magicVulnerability.${rollAbility2}`), effectiveConditionData)) {
    disadvantage = true;
    magicVulnerability = true;
  }
  if (!advantage) advantage = void 0;
  if (!disadvantage) disadvantage = void 0;
  if (debugEnabled > 1) debug(`${actor.name} resistant to magic : ${advantage}`);
  if (debugEnabled > 1) debug(`${actor.name} vulnerable to magic : ${disadvantage}`);
  return { advantage, disadvantage, magicResistance, magicVulnerability };
}
__name(checkMagicResistanceAndVulnerability, "checkMagicResistanceAndVulnerability");
function evalCondition(condition, conditionData, options = { errorReturn: false, async: false }) {
  if (typeof condition === "number" || typeof condition === "boolean") return condition;
  if (condition === void 0 || condition === "" || typeof condition !== "string") return options?.errorReturn;
  let returnValue;
  try {
    if (condition.includes("@")) {
      condition = Roll.replaceFormulaData(condition, conditionData, { missing: "0" });
    }
    if (options?.async) returnValue = asyncMySafeEval(condition, conditionData, options?.errorReturn);
    else returnValue = mySafeEval(condition, conditionData, options?.errorReturn ?? false);
    if (debugEnabled > 0) warn("evalCondition ", returnValue, condition, conditionData);
  } catch (err) {
    returnValue = options?.errorReturn ?? false;
    const message = `midi-qol | evalCondition | activation condition (${condition}) error, actorUuid: ${conditionData.actorUuid} itemUuid: ${conditionData.item?.uuid} targetUuid: ${conditionData.targetUuid}`;
    TroubleShooter.recordError(err, message);
    console.warn(message, err, conditionData);
  }
  return returnValue;
}
__name(evalCondition, "evalCondition");
function computeTemplateShapeDistance(templateDocument) {
  let { x, y, direction, distance } = templateDocument;
  if (!canvas || !canvas.scene) return { shape: void 0, distance: 0 };
  distance ??= 0;
  distance *= canvas.dimensions?.distancePixels ?? 1;
  direction = Math.toRadians(direction);
  if (!templateDocument.object) {
    throw new Error("Template document has no object");
  }
  templateDocument.object.ray = foundry.canvas.geometry.Ray.fromAngle(x, y, direction, distance);
  let shape;
  templateDocument.object.shape = templateDocument.object._computeShape();
  return { shape: templateDocument.object.shape, distance: templateDocument.distance };
}
__name(computeTemplateShapeDistance, "computeTemplateShapeDistance");
var _enableNotifications = true;
function notificationNotify(wrapped, ...args) {
  if (_enableNotifications) return wrapped(...args);
}
__name(notificationNotify, "notificationNotify");
function enableNotifications(enable) {
  _enableNotifications = enable;
}
__name(enableNotifications, "enableNotifications");
function getStatusName(statusId) {
  if (!statusId) return "undefined";
  const se = CONFIG.statusEffects.find((efData) => efData.id === statusId);
  return i18n(se?.name ?? statusId) ?? `${statusId}`;
}
__name(getStatusName, "getStatusName");
function getWoundedStatus() {
  let condition = CONFIG.statusEffects.find((efData) => efData.id === configSettings.midiWoundedCondition);
  if (condition || !ceInterface) return condition;
  return ceInterface.findEffect({ effectId: configSettings.midiWoundedCondition?.replace("zce-", "ce-") });
}
__name(getWoundedStatus, "getWoundedStatus");
function getUnconsciousStatus() {
  let condition = CONFIG.statusEffects.find((efData) => efData.id === configSettings.midiUnconsciousCondition);
  if (condition || !ceInterface) return condition;
  return ceInterface.findEffect({ effectId: configSettings.midiUnconsciousCondition?.replace("zce-", "ce-") });
}
__name(getUnconsciousStatus, "getUnconsciousStatus");
function getDeadStatus() {
  let condition = CONFIG.statusEffects.find((efData) => efData.id === configSettings.midiDeadCondition);
  if (condition || !ceInterface) return condition;
  return ceInterface.findEffect({ effectId: configSettings.midiDeadCondition?.replace("zce-", "ce-") });
}
__name(getDeadStatus, "getDeadStatus");
function getVitalityDeadStatus() {
  const conditionId = configSettings.optionalRules.vitalityDeadCondition || "dead";
  let condition = CONFIG.statusEffects.find((efData) => efData.id === conditionId);
  if (condition || !ceInterface) return condition;
  return ceInterface.findEffect({ effectId: conditionId?.replace("zce-", "ce-") });
}
__name(getVitalityDeadStatus, "getVitalityDeadStatus");
function isInCombat(actor) {
  const actorUuid = actor.uuid;
  let combats;
  if (actorUuid.startsWith("Scene")) {
    const tokenId = actorUuid.split(".")[3];
    combats = game.combats?.combats.filter(
      (combat) => combat.combatants.filter((combatant) => combatant?.tokenId === tokenId).length !== 0
    );
  } else {
    const actorId = actor.id;
    combats = game.combats?.combats.filter(
      (combat) => combat.combatants.filter((combatant) => combatant?.actorId === actorId).length !== 0
    );
  }
  return (combats?.length ?? 0) > 0;
}
__name(isInCombat, "isInCombat");
async function setActionUsed(actor) {
  await actor.setFlag(MODULE_ID, "actions", { action: true });
}
__name(setActionUsed, "setActionUsed");
async function setReactionUsed(actor, active = true) {
  if (!getReactionEffect()) return;
  const id = "reaction";
  if (!active) {
    await actor.effects.get(getStaticID(id))?.delete();
  } else {
    if (!["all", "displayOnly"].includes(configSettings.enforceReactions) && configSettings.enforceReactions !== actor.type) return;
    const actions = actor.getFlag(MODULE_ID, "actions");
    const newCount = (actions?.reactionsUsed ?? 0) + 1;
    await actor.effects.get(getStaticID(id))?.delete();
    const effect = foundry.utils.deepClone(getReactionEffect());
    const allReactionsUsed = newCount >= (actor.getFlag(MODULE_ID, "actions")?.reactionsMax ?? 1);
    if (!effect) return;
    effect.updateSource({
      origin: actor.uuid,
      changes: [
        { key: "flags.midi-qol.actions.reaction", mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, value: String(allReactionsUsed) },
        { key: "flags.midi-qol.actions.reactionsUsed", mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, value: String(newCount) },
        { key: "flags.midi-qol.actions.reactionCombatRound", mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, value: String(game.combat?.round ?? false) }
      ]
    });
    if (actions?.reactionsReset === "eachTurn") {
      effect.updateSource({ duration: { turns: 1 } });
    }
    if (actions?.reactionsReset === "rest") {
      effect.updateSource({ flags: { dae: { specialDuration: ["shortRest"] } } });
    }
    if (actions?.reactionsReset === "never") {
      effect.updateSource({ flags: { dae: { specialDuration: [] } } });
    }
    if (await asyncHooksCall2("midi-qol.preSetReactionUsed", actor, effect) === false) return;
    await ActiveEffect.implementation.create(effect.toObject(), { parent: actor, keepId: true });
    await asyncHooksCallAll("midi-qol.setReactionUsed", actor, effect);
  }
}
__name(setReactionUsed, "setReactionUsed");
async function setBonusActionUsed(actor) {
  if (debugEnabled > 0) warn("setBonusActionUsed | starting");
  if (!["all", "displayOnly"].includes(configSettings.enforceBonusActions) && configSettings.enforceBonusActions !== actor.type) return;
  const id = "bonusaction";
  const actions = actor.getFlag(MODULE_ID, "actions");
  const newCount = (actions?.bonusActionsUsed ?? 0) + 1;
  const bonusActionsMax = actions?.bonusActionsMax ?? 1;
  const allBonusActionsUsed = newCount >= bonusActionsMax;
  await actor.effects.get(getStaticID(id))?.delete();
  const effect = foundry.utils.deepClone(getBonusActionEffect());
  if (!effect) return;
  effect.updateSource({
    origin: actor.uuid,
    changes: [
      { key: "flags.midi-qol.actions.bonus", mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, value: String(allBonusActionsUsed) },
      { key: "flags.midi-qol.actions.bonusActionsUsed", mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, value: String(newCount) },
      { key: "flags.midi-qol.actions.bonusActionCombatRound", mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, value: String(game.combat?.round ?? false) }
    ]
  });
  if (actions?.bonusActionsReset === "eachTurn") {
    effect.updateSource({ duration: { turns: 1 } });
  }
  if (actions?.bonusActionsReset === "rest") {
    effect.updateSource({ flags: { dae: { specialDuration: ["shortRest"] } } });
  }
  if (actions?.bonusActionsReset === "never") {
    effect.updateSource({ flags: { dae: { specialDuration: [] } } });
  }
  effect.origin = actor.uuid;
  if (await asyncHooksCall2("midi-qol.preSetBonusActionUsed", actor, effect) === false) return;
  await ActiveEffect.implementation.create(effect, { parent: actor, keepId: true });
  await asyncHooksCallAll("midi-qol.setBonusActionUsed", actor, effect);
}
__name(setBonusActionUsed, "setBonusActionUsed");
async function removeActionUsed(actor) {
  if (game.user?.isGM || actor.isOwner) return await actor?.setFlag(MODULE_ID, "actions", { action: false });
  else return await unTimedExecuteAsGM("_gmSetFlag", { base: MODULE_ID, key: "actions.action", value: false, actorUuid: actor.uuid });
}
__name(removeActionUsed, "removeActionUsed");
async function removeReactionUsed(actor, force = false) {
  if (debugEnabled > 0) warn("removeReactionUsed | starting", actor);
  if (force || !installedModules.get("times-up")) {
    await actor.effects.get(getStaticID("reaction"))?.delete();
  }
  if (actor.flags?.[MODULE_ID]?.actions?.reaction) await actor.update({ flags: { [MODULE_ID]: { actions: { reactionUsed: 0, "-=reactionCombatRound": null } } } });
}
__name(removeReactionUsed, "removeReactionUsed");
function hasUsedAction(actor) {
  return actor?.getFlag(MODULE_ID, "actions")?.action;
}
__name(hasUsedAction, "hasUsedAction");
function hasUsedReaction(actor) {
  return (actor.getFlag(MODULE_ID, "actions")?.reactionsUsed ?? 0) >= (actor.getFlag(MODULE_ID, "actions")?.reactionsMax ?? 1);
}
__name(hasUsedReaction, "hasUsedReaction");
function hasUsedAnyReaction(actor) {
  return (actor.getFlag(MODULE_ID, "actions")?.reactionsUsed ?? 0) > 0;
}
__name(hasUsedAnyReaction, "hasUsedAnyReaction");
async function expirePerTurnBonusActions(combat, data, options) {
  const optionalFlagRe = /flags.midi-qol.optional.[^.]+.(count|countAlt)$/;
  for (let combatant of combat.turns ?? []) {
    const actor = combatant.actor;
    if (!actor) continue;
    const actorAllApplicableEffects = actor.allApplicableEffects();
    for (let effect of actorAllApplicableEffects) {
      for (let change of effect.changes) {
        if (change.key.match(optionalFlagRe) && (change.value === "each-turn" || change.value === "each-round" && data.round !== combat.round) || change.value === "turn" && data.turn !== null && combat?.turns[data.turn ?? combat.turn].actor === actor) {
          const usedKey = change.key.replace(/.(count|countAlt)$/, ".used");
          const isUsed = foundry.utils.getProperty(actor, usedKey);
          if (isUsed) {
            const key = usedKey.replace(`flags.${MODULE_ID}.`, "");
            await unTimedExecuteAsGM("_gmUnsetFlag", { actorUuid: actor.uuid, base: MODULE_ID, key });
          }
        }
      }
    }
  }
}
__name(expirePerTurnBonusActions, "expirePerTurnBonusActions");
function hasUsedBonusAction(actor) {
  const currentUsed = actor.getFlag(MODULE_ID, "actions")?.bonusActionsUsed ?? 0;
  const max = actor.getFlag(MODULE_ID, "actions")?.bonusActionsMax ?? 1;
  return currentUsed >= max;
}
__name(hasUsedBonusAction, "hasUsedBonusAction");
async function removeBonusActionUsed(actor, force = false) {
  if (force || !installedModules.get("times-up")) {
    await actor.effects.get(getStaticID("bonusaction"))?.delete();
  }
  if (force) await actor.update({ flags: { [MODULE_ID]: { actions: { bonusActionsUsed: 0, bonus: false, "-=bonusActionCombatRound": null } } } });
}
__name(removeBonusActionUsed, "removeBonusActionUsed");
function needsReactionCheck(actor) {
  return configSettings.enforceReactions === "all" || configSettings.enforceReactions === actor.type;
}
__name(needsReactionCheck, "needsReactionCheck");
function needsAOOCheck(actor) {
  return configSettings.recordAOO === "all" || configSettings.recordAOO === actor.type;
}
__name(needsAOOCheck, "needsAOOCheck");
function needsBonusActionCheck(actor) {
  return configSettings.enforceBonusActions === "all" || configSettings.enforceBonusActions === actor.type;
}
__name(needsBonusActionCheck, "needsBonusActionCheck");
async function asyncHooksCallAll(hook, ...args) {
  if (CONFIG.debug.hooks) {
    console.log(`DEBUG | midi-qol async Calling ${hook} hook with args:`);
    console.log(args);
  }
  const hookEvents = Hooks.events[hook];
  if (debugEnabled > 1) debug("asyncHooksCall", hook, "hookEvents:", hookEvents, args);
  if (!hookEvents) return void 0;
  if (debugEnabled > 0) {
    warn(`asyncHooksCall calling ${hook}`, hookEvents, args);
  }
  for (let entry of Array.from(hookEvents)) {
    try {
      if (debugEnabled > 1) {
        log(`asyncHooksCall for Hook ${hook} calling`, entry, args);
      }
      await hookCall(entry, args);
    } catch (err) {
      const message = `hooked function for hook ${hook}`;
      error2(message, err);
      TroubleShooter.recordError(err, message);
    }
  }
  return true;
}
__name(asyncHooksCallAll, "asyncHooksCallAll");
async function callActivityHooks(activity, hookName, ...args) {
  if (await asyncHooksCall2(hookName, ...args) === false) return false;
  if (await asyncHooksCall2(`${hookName}.${activity.item.uuid}`, ...args) === false) return false;
  if (await asyncHooksCall2(`${hookName}.${activity.uuid}`, ...args) === false) return false;
  const premadesHook = hookName.replace("midi-qol.", "midi-qol.premades.");
  if (await asyncHooksCall2(premadesHook, ...args) === false) return false;
  if (await asyncHooksCall2(`${premadesHook}.${activity.item.uuid}`, ...args) === false) return false;
  if (await asyncHooksCall2(`${premadesHook}.${activity.uuid}`, ...args) === false) return false;
  return true;
}
__name(callActivityHooks, "callActivityHooks");
async function asyncHooksCall2(hook, ...args) {
  if (CONFIG.debug.hooks) {
    console.log(`DEBUG | midi-qol async Calling ${hook} hook with args:`);
    console.log(args);
  }
  const hookEvents = Hooks.events[hook];
  if (debugEnabled > 1) log("asyncHooksCall", hook, "hookEvents:", hookEvents, args);
  if (!hookEvents) return void 0;
  if (debugEnabled > 0) {
    warn(`asyncHooksCall calling ${hook}`, args, hookEvents);
  }
  for (let entry of Array.from(hookEvents)) {
    let callAdditional;
    try {
      if (debugEnabled > 1) {
        log(`asyncHooksCall for Hook ${hook} calling`, entry, args);
      }
      callAdditional = await hookCall(entry, args);
    } catch (err) {
      const message = `midi-qol | hooked function for hook ${hook} error`;
      error2(message, err, entry);
      TroubleShooter.recordError(err, message);
      callAdditional = true;
    }
    if (callAdditional === false) return false;
  }
  return true;
}
__name(asyncHooksCall2, "asyncHooksCall");
function hookCall(entry, args) {
  const { hook, id, fn, once } = entry;
  if (once) Hooks.off(hook, id);
  try {
    return entry.fn(...args);
  } catch (err) {
    const message = `Error thrown in hooked function '${fn?.name}' for hook '${hook}'`;
    TroubleShooter.recordError(err, message);
    error2(`midi | ${message}`);
    if (hook !== "error") Hooks.onError("Hooks.#call", err, { message, hook, fn, log: "error" });
  }
}
__name(hookCall, "hookCall");
var attributionTypeLocalization = {
  "ADV": "midi-qol.AttribADV",
  "DIS": "midi-qol.AttribDIS",
  "NOADV": "midi-qol.AttribNOADV",
  "NODIS": "midi-qol.AttribNODIS",
  "CRIT": "midi-qol.AttribCRIT",
  "NOCRIT": "midi-qol.AttribNOCRIT",
  "FUMBLE": "midi-qol.AttribFUMBLE",
  "NOFUMBLE": "midi-qol.AttribNOFUMBLE",
  "SUCCESS": "midi-qol.AttribSUCCESS",
  "FAIL": "midi-qol.AttribFAIL",
  "MIN": "midi-qol.AttribMIN",
  "MAX": "midi-qol.AttribMAX"
};
function attributionMapToArray(map) {
  const entries = [];
  for (const [type, sources] of Object.entries(map)) {
    if (sources) {
      for (const [source, displayName] of Object.entries(sources)) {
        entries.push({ type, source, displayName });
      }
    }
  }
  return entries;
}
__name(attributionMapToArray, "attributionMapToArray");
function addAttribution(roll, attributions) {
  if (attributions && Object.keys(attributions).length > 0) {
    const entries = attributionMapToArray(attributions);
    foundry.utils.setProperty(roll, "options.attributions", entries);
  }
}
__name(addAttribution, "addAttribution");
function getAdvantageModeTitle(mode) {
  const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
  if (mode === ADV_MODE.ADVANTAGE) return i18n("DND5E.Advantage");
  if (mode === ADV_MODE.DISADVANTAGE) return i18n("DND5E.Disadvantage");
  return i18n("DND5E.Normal");
}
__name(getAdvantageModeTitle, "getAdvantageModeTitle");
async function renderAttributionTooltip(attributions, title) {
  const entries = attributionMapToArray(attributions);
  const localizedEntries = entries.map((attr) => ({
    ...attr,
    localizedType: i18n(attributionTypeLocalization[attr.type] ?? attr.type)
  }));
  return foundry.applications.handlebars.renderTemplate(
    "modules/midi-qol/templates/attribution-tooltip.html",
    { attributions: localizedEntries, title }
  );
}
__name(renderAttributionTooltip, "renderAttributionTooltip");
function getTooltip(roll, options = {}) {
  const parts = roll.dice?.map((d) => d.getTooltipData()) ?? [];
  const rawAttributions = roll.options?.attributions ?? [];
  const attributions = rawAttributions.map((attr) => ({
    ...attr,
    localizedType: i18n(attributionTypeLocalization[attr.type] ?? attr.type)
  }));
  const templateData = {
    // @ts-expect-error
    advTooltip: roll.options?.advTooltip,
    attributions,
    tooltipFormula: options?.tooltipFormula ?? false,
    formula: roll.formula,
    parts
  };
  return foundry.applications.handlebars.renderTemplate("modules/midi-qol/templates/tooltip.html", templateData);
}
__name(getTooltip, "getTooltip");
async function midiRenderRoll(roll) {
  return roll.render();
}
__name(midiRenderRoll, "midiRenderRoll");
async function midiRenderAttackRoll(roll, options) {
  options = foundry.utils.mergeObject(options ?? {}, { tooltipFormula: ["formula", "formulaadv"].includes(configSettings.rollAlternate) });
  return midiRenderTemplateRoll(roll, "modules/midi-qol/templates/attack-roll.html", options);
}
__name(midiRenderAttackRoll, "midiRenderAttackRoll");
async function midiRenderDamageRoll(roll, options) {
  options = foundry.utils.mergeObject(options ?? {}, { tooltipFormula: ["formula", "formulaadv"].includes(configSettings.rollAlternate) });
  let html = midiRenderTemplateRoll(roll, "modules/midi-qol/templates/damage-roll.html", options);
  return html;
}
__name(midiRenderDamageRoll, "midiRenderDamageRoll");
function midiRenderOtherDamageRoll(roll, options) {
  options = foundry.utils.mergeObject(options ?? {}, { tooltipFormula: ["formula", "formulaadv"].includes(configSettings.rollAlternate) });
  let html = midiRenderTemplateRoll(roll, "modules/midi-qol/templates/other-damage-roll.html", options);
  return html;
}
__name(midiRenderOtherDamageRoll, "midiRenderOtherDamageRoll");
function midiRenderBonusDamageRoll(roll, options) {
  options = foundry.utils.mergeObject(options ?? {}, { tooltipFormula: ["formula", "formulaadv"].includes(configSettings.rollAlternate) });
  let html = midiRenderTemplateRoll(roll, "modules/midi-qol/templates/bonus-damage-roll.html", options);
  return html;
}
__name(midiRenderBonusDamageRoll, "midiRenderBonusDamageRoll");
function midiRenderFormulaRoll(roll, options) {
  options = foundry.utils.mergeObject(options ?? {}, { tooltipFormula: ["formula", "formulaadv"].includes(configSettings.rollAlternate) });
  return midiRenderTemplateRoll(roll, "modules/midi-qol/templates/roll.html", options);
}
__name(midiRenderFormulaRoll, "midiRenderFormulaRoll");
async function midiRenderTemplateRoll(roll, template, options) {
  if (!roll) return "";
  const advantageMode = roll.options?.advantageMode;
  let advantageModeStr;
  if (advantageMode === 1) advantageModeStr = "advantage";
  else if (advantageMode === -1) advantageModeStr = "disadvantage";
  let attributionTooltip = "";
  const rawAttributions = roll.options?.attributions ?? [];
  if (rawAttributions.length > 0) {
    const attributions = rawAttributions.map((attr) => ({
      ...attr,
      localizedType: i18n(attributionTypeLocalization[attr.type] ?? attr.type)
    }));
    const title = advantageModeStr === "advantage" ? i18n("DND5E.Advantage") : advantageModeStr === "disadvantage" ? i18n("DND5E.Disadvantage") : "";
    attributionTooltip = await foundry.applications.handlebars.renderTemplate(
      "modules/midi-qol/templates/attribution-tooltip.html",
      { attributions, title }
    );
  }
  const damageType = roll.options?.type;
  const damageConfig = damageType ? GameSystemConfig.damageTypes[damageType] ?? GameSystemConfig.healingTypes?.[damageType] : void 0;
  const chatData = {
    formula: roll.formula,
    user: game.user?.id,
    tooltip: await getTooltip(roll, options),
    tooltipFormula: options?.tooltipFormula ?? false,
    // @ts-expect-error no dnd5e-types
    flavor: options?.flavor ?? roll.options?.flavor,
    total: roll.total !== void 0 ? Math.round(roll.total * 100) / 100 : "???",
    advantageMode: advantageModeStr,
    attributionTooltip,
    damageTypeIcon: damageConfig?.icon,
    damageTypeLabel: damageConfig?.label
  };
  return foundry.applications.handlebars.renderTemplate(template, chatData);
}
__name(midiRenderTemplateRoll, "midiRenderTemplateRoll");
function heightIntersects(targetDocument, flankerDocument) {
  const targetElevation = targetDocument.elevation ?? 0;
  const flankerElevation = flankerDocument.elevation ?? 0;
  const targetTopElevation = targetElevation + Math.max(targetDocument.height, targetDocument.width) * (canvas.dimensions?.distance ?? 5);
  const flankerTopElevation = flankerElevation + Math.min(flankerDocument.height, flankerDocument.width) * (canvas.dimensions?.distance ?? 5);
  if (flankerTopElevation < targetElevation || flankerElevation > targetTopElevation) return false;
  return true;
}
__name(heightIntersects, "heightIntersects");
function isCellAdjacentToToken(cellPoint, target, maxDistCells = 1) {
  if (!canvas?.grid) return false;
  const gridW = canvas.grid.sizeX;
  const gridH = canvas.grid.sizeY;
  const cellTopLeft = canvas.grid.getTopLeftPoint(cellPoint);
  const cellLeft = cellTopLeft.x;
  const cellTop = cellTopLeft.y;
  const cellRight = cellLeft + gridW;
  const cellBottom = cellTop + gridH;
  const targetLeft = target.x;
  const targetTop = target.y;
  const targetRight = target.x + target.document.width * gridW;
  const targetBottom = target.y + target.document.height * gridH;
  const dx = Math.max(0, cellLeft - targetRight, targetLeft - cellRight);
  const dy = Math.max(0, cellTop - targetBottom, targetTop - cellBottom);
  const maxGap = (maxDistCells - 1) * Math.max(gridW, gridH);
  return dx <= maxGap && dy <= maxGap;
}
__name(isCellAdjacentToToken, "isCellAdjacentToToken");
function getFlankingDistance(t1, t2) {
  const gridDist = canvas?.dimensions?.distance ?? 5;
  const fudge = configSettings.gridlessFudge ?? 0;
  if (canvas?.scene?.grid.type === CONST.GRID_TYPES.GRIDLESS) {
    const raw = computePerimeterDistance(t1, t2, { wallsBlock: true, includeElevation: false });
    if (raw < 0) return -1;
    return gridDist + Math.max(0, raw - fudge);
  }
  return getDistance(t1, t2, { wallsBlock: true });
}
__name(getFlankingDistance, "getFlankingDistance");
function findPotentialFlankers(target) {
  const gridDist = canvas?.dimensions?.distance ?? 5;
  const isGridless = canvas?.scene?.grid.type === CONST.GRID_TYPES.GRIDLESS;
  const requiresVisibility = configSettings.optionalRules.flankingRequiresVisibility;
  let allies;
  let reachAllies;
  if (isGridless) {
    const canvasPlaceables = canvas?.tokens?.placeables ?? [];
    const allNearby = canvasPlaceables.filter((t) => {
      if (!isValidTarget(t)) return false;
      if (!t.actor) return false;
      if (t.id === target.id) return false;
      if (checkIncapacitated(t.actor, debugEnabled > 0, false)) return false;
      if (t.document.disposition === target.document.disposition) return false;
      if (requiresVisibility && !canSense(target, t)) return false;
      const dist = getFlankingDistance(t, target);
      return dist >= 0 && dist <= gridDist;
    });
    const reachNearby = canvasPlaceables.filter((t) => {
      if (!isValidTarget(t)) return false;
      if (!t.actor) return false;
      if (t.id === target.id) return false;
      if (checkIncapacitated(t.actor, debugEnabled > 0, false)) return false;
      if (t.document.disposition === target.document.disposition) return false;
      if (requiresVisibility && !canSense(target, t)) return false;
      if (allNearby.some((a) => a === t)) return false;
      if (!t.actor.items.contents.some((item) => item.system?.properties?.has("rch") && item.system.equipped)) return false;
      const dist = getFlankingDistance(t, target);
      return dist >= 0 && dist <= 2 * gridDist;
    });
    allies = allNearby.concat(reachNearby);
  } else {
    allies = findNearby(-1, target, gridDist, requiresVisibility ? { isSeen: true } : {});
    reachAllies = findNearby(-1, target, 2 * gridDist, requiresVisibility ? { isSeen: true } : {}).filter(
      (ally) => !allies.some((tk) => tk === ally) && //@ts-expect-error no dnd5e-types
      ally.actor?.items.contents.some((item) => item.system?.properties?.has("rch") && item.system.equipped)
    );
    allies = allies.concat(reachAllies);
  }
  return allies;
}
__name(findPotentialFlankers, "findPotentialFlankers");
function getHexCentroid(target) {
  if (!canvas?.grid?.isHexagonal) return target.center;
  const offsets = target.document.getOccupiedGridSpaceOffsets();
  if (!offsets.length) return target.center;
  let sumX = 0, sumY = 0;
  for (const offset of offsets) {
    const center = canvas.grid.getCenterPoint(offset);
    sumX += center.x;
    sumY += center.y;
  }
  return { x: sumX / offsets.length, y: sumY / offsets.length };
}
__name(getHexCentroid, "getHexCentroid");
function isAngleFlanking(token, ally, target) {
  const referencePoint = canvas?.grid?.isHexagonal ? getHexCentroid(target) : target.center;
  const angleToken = Math.atan2(token.center.y - referencePoint.y, token.center.x - referencePoint.x);
  const angleAlly = Math.atan2(ally.center.y - referencePoint.y, ally.center.x - referencePoint.x);
  let diff = Math.abs(angleToken - angleAlly);
  if (diff > Math.PI) diff = 2 * Math.PI - diff;
  const MIN_FLANKING_ANGLE = 150 / 180 * Math.PI;
  return diff >= MIN_FLANKING_ANGLE;
}
__name(isAngleFlanking, "isAngleFlanking");
async function computeFlankedStatus(target) {
  if (!checkRule("checkFlanking") || !["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(checkRule("checkFlanking"))) return false;
  if (!canvas || !target) return false;
  const allies = findPotentialFlankers(target);
  if (allies.length <= 1) return false;
  const gridW = canvas.grid?.sizeX ?? 100;
  const gridH = canvas.grid?.sizeY ?? 100;
  const tl = { x: target.x, y: target.y };
  const tr = { x: target.x + target.document.width * gridW, y: target.y };
  const bl = { x: target.x, y: target.y + target.document.height * gridH };
  const br = { x: target.x + target.document.width * gridW, y: target.y + target.document.height * gridH };
  const top = [tl.x, tl.y, tr.x, tr.y];
  const bottom = [bl.x, bl.y, br.x, br.y];
  const left = [tl.x, tl.y, bl.x, bl.y];
  const right = [tr.x, tr.y, br.x, br.y];
  while (allies.length > 1) {
    const token = allies.pop();
    if (!token) break;
    if (!heightIntersects(target.document, token.document)) continue;
    if (checkRule("checkFlanking") === "ceflankedNoconga" && installedModules.get("dfreds-convenient-effects")) {
      const CEFlanked = getFlankedEffect();
      const hasFlanked = token.actor && CEFlanked && CEHasEffectApplied({ effectName: CEFlanked.name, uuid: token.actor.uuid });
      if (hasFlanked) continue;
    } else if (checkRule("checkFlanking") === "midiFlankedNoConga") {
      const hasFlanked = token.actor && hasCondition(token.actor, "flanked");
      if (hasFlanked) continue;
    }
    if (token.actor?.flags?.[MODULE_ID]?.canFlank !== void 0) {
      const conditionData = createConditionData({ actor: token.actor, token, target });
      const tokenCanFlank = await evalCondition(token.actor.flags[MODULE_ID]?.canFlank, conditionData, void 0);
      if (tokenCanFlank === false) continue;
    }
    const tokenStartX = -Math.max(0, token.document.width / 2 - 0.5);
    const tokenStartY = -Math.max(0, token.document.height / 2 - 0.5);
    const tokenPoints = [];
    for (let x = tokenStartX; x < token.document.width / 2; x++) {
      for (let y = tokenStartY; y < token.document.height / 2; y++) {
        let tx = token.center.x + x * gridW;
        let ty = token.center.y + y * gridH;
        if (!isCellAdjacentToToken({ x: tx, y: ty }, target)) continue;
        tokenPoints.push({ x: tx, y: ty });
      }
    }
    for (let ally of allies) {
      if (ally.document.uuid === token.document.uuid) continue;
      const actor = ally.actor;
      if (actor?.system.attributes?.hp?.value <= 0) continue;
      if (!heightIntersects(target.document, ally.document)) continue;
      if (hasCondition(actor, "incapacitated")) continue;
      if (checkRule("checkFlanking") === "ceflankedNoconga" && installedModules.get("dfreds-convenient-effects")) {
        const CEFlanked = getFlankedEffect();
        const hasFlanked = CEFlanked && CEHasEffectApplied({ effectName: CEFlanked.name, uuid: ally.actor?.uuid ?? "" });
        if (hasFlanked) continue;
      }
      if (checkRule("checkFlanking") === "midiFlankedNoConga") {
        const hasFlanked = hasCondition(actor, "flanked");
        if (hasFlanked) continue;
      }
      if (canvas.grid?.isHexagonal || canvas.scene?.grid.type === CONST.GRID_TYPES.GRIDLESS) {
        if (isAngleFlanking(token, ally, target)) return true;
        continue;
      }
      const allyStartX = -Math.max(0, ally.document.width / 2 - 0.5);
      const allyStartY = -Math.max(0, ally.document.height / 2 - 0.5);
      const allyPoints = [];
      for (let x1 = allyStartX; x1 < ally.document.width / 2; x1++) {
        for (let y1 = allyStartY; y1 < ally.document.height / 2; y1++) {
          let ax = ally.center.x + x1 * gridW;
          let ay = ally.center.y + y1 * gridH;
          if (!isCellAdjacentToToken({ x: ax, y: ay }, target)) continue;
          allyPoints.push({ x: ax, y: ay });
        }
      }
      for (let tokenPoint of tokenPoints) {
        for (let allyPoint of allyPoints) {
          const p1 = canvas.grid?.getCenterPoint(tokenPoint);
          const p2 = canvas.grid?.getCenterPoint(allyPoint);
          if (!p1 || !p2) continue;
          const rayToCheck = new foundry.canvas.geometry.Ray(p1, p2);
          const flankingTop = rayToCheck.intersectSegment(top) && rayToCheck.intersectSegment(bottom);
          const flankingLeft = rayToCheck.intersectSegment(left) && rayToCheck.intersectSegment(right);
          if (flankingLeft || flankingTop) {
            return true;
          }
        }
      }
    }
  }
  return false;
}
__name(computeFlankedStatus, "computeFlankedStatus");
async function computeFlankingStatus(token, target) {
  if (!checkRule("checkFlanking") || checkRule("checkFlanking") === "off") return false;
  if (!canvas) return false;
  if (!token) return false;
  if (!target) return false;
  if (!heightIntersects(target.document, token.document)) return false;
  let range = 1;
  if (token.actor?.items.contents.some((item) => item.system?.properties?.has("rch") && item.system.equipped)) {
    range = 2;
  }
  const flankDist = getFlankingDistance(token, target);
  if (flankDist > range * (canvas.dimensions?.distance ?? 5)) return false;
  const allies = findPotentialFlankers(target);
  if (!token.document.disposition) return false;
  if (allies.length <= 1) return false;
  const gridW = canvas.grid?.sizeX ?? 100;
  const gridH = canvas.grid?.sizeY ?? 100;
  const tl = { x: target.x, y: target.y };
  const tr = { x: target.x + target.document.width * gridW, y: target.y };
  const bl = { x: target.x, y: target.y + target.document.height * gridH };
  const br = { x: target.x + target.document.width * gridW, y: target.y + target.document.height * gridH };
  const top = [tl.x, tl.y, tr.x, tr.y];
  const bottom = [bl.x, bl.y, br.x, br.y];
  const left = [tl.x, tl.y, bl.x, bl.y];
  const right = [tr.x, tr.y, br.x, br.y];
  const tokenStartX = -Math.max(0, token.document.width / 2 - 0.5);
  const tokenStartY = -Math.max(0, token.document.height / 2 - 0.5);
  const tokenPoints = [];
  for (let x = tokenStartX; x < token.document.width / 2; x++) {
    for (let y = tokenStartY; y < token.document.height / 2; y++) {
      let tx = token.center.x + x * gridW;
      let ty = token.center.y + y * gridH;
      if (!isCellAdjacentToToken({ x: tx, y: ty }, target)) continue;
      tokenPoints.push({ x: tx, y: ty });
    }
  }
  for (let ally of allies) {
    if (ally.document.uuid === token.document.uuid) continue;
    if (!heightIntersects(ally.document, target.document)) continue;
    const actor = ally.actor;
    if (checkIncapacitated(ally.actor, debugEnabled > 0, false)) continue;
    if (hasCondition(actor, "incapacitated")) continue;
    if (ally.actor?.flags?.[MODULE_ID]?.canFlank !== void 0) {
      const conditionData = createConditionData({ actor: ally.actor, token: ally, target });
      const allyCanFlank = await evalCondition(ally.actor.flags[MODULE_ID]?.canFlank, conditionData, void 0);
      if (allyCanFlank === false) continue;
    }
    if (canvas.grid?.isHexagonal || canvas.scene?.grid.type === CONST.GRID_TYPES.GRIDLESS) {
      if (isAngleFlanking(token, ally, target)) return true;
      continue;
    }
    const allyStartX = -Math.max(0, ally.document.width / 2 - 0.5);
    const allyStartY = -Math.max(0, ally.document.height / 2 - 0.5);
    const allyPoints = [];
    for (let x1 = allyStartX; x1 < ally.document.width / 2; x1++) {
      for (let y1 = allyStartY; y1 < ally.document.height / 2; y1++) {
        let ax = ally.center.x + x1 * gridW;
        let ay = ally.center.y + y1 * gridH;
        if (!isCellAdjacentToToken({ x: ax, y: ay }, target)) {
          continue;
        }
        allyPoints.push({ x: ax, y: ay });
      }
    }
    for (let tokenPoint of tokenPoints) {
      for (let allyPoint of allyPoints) {
        const p1 = canvas.grid?.getCenterPoint(tokenPoint);
        const p2 = canvas.grid?.getCenterPoint(allyPoint);
        if (!p1 || !p2) continue;
        const rayToCheck = new foundry.canvas.geometry.Ray(p1, p2);
        const flankingTop = rayToCheck.intersectSegment(top) && rayToCheck.intersectSegment(bottom);
        const flankingLeft = rayToCheck.intersectSegment(left) && rayToCheck.intersectSegment(right);
        if (flankingLeft || flankingTop) {
          return true;
        }
      }
    }
  }
  return false;
}
__name(computeFlankingStatus, "computeFlankingStatus");
function getFlankingEffect() {
  return ceInterface?.findEffect({ effectName: "Flanking" });
}
__name(getFlankingEffect, "getFlankingEffect");
function getFlankedEffect() {
  return ceInterface?.findEffect({ effectName: "Flanked" });
}
__name(getFlankedEffect, "getFlankedEffect");
function getReactionEffect() {
  if (!midiReactionEffect) return void 0;
  return getCPREffect(midiReactionEffect._id) ?? midiReactionEffect;
}
__name(getReactionEffect, "getReactionEffect");
function getBonusActionEffect() {
  if (!midiBonusActionEffect) return void 0;
  return getCPREffect(midiBonusActionEffect._id) ?? midiBonusActionEffect;
}
__name(getBonusActionEffect, "getBonusActionEffect");
async function markFlanking(token, target) {
  if (!canvas) return false;
  if (!target || !target?.actor) return false;
  const flankingMode = checkRule("checkFlanking");
  if (!flankingMode || flankingMode === "off") return false;
  const isFlankingMode = ["advonly", "ceonly", "midiFlanking"].includes(flankingMode);
  const isFlankedMode = ["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(flankingMode);
  const skipAttackerStatus = (configSettings.optionalRules?.flankingBonus ?? "advantage") !== "advantage";
  if (isFlankingMode) {
    if (!token || token === target) return false;
    const needsFlanking = await computeFlankingStatus(token, target);
    if (skipAttackerStatus) return needsFlanking;
    if (flankingMode === "ceonly") {
      if (installedModules.get("dfreds-convenient-effects")) {
        let CEFlanking = getFlankingEffect();
        if (!CEFlanking) return needsFlanking;
        const hasFlanking = CEHasEffectApplied({ effectName: CEFlanking.name ?? "Flanking", uuid: token.actor?.uuid ?? "" });
        if (needsFlanking && !hasFlanking && token.actor) {
          await CEAddEffectWith2({ effectName: CEFlanking.name ?? "Flanking", uuid: token.actor.uuid, overlay: false });
        } else if (!needsFlanking && hasFlanking && token.actor) {
          await CERemoveEffect({ effectName: CEFlanking.name ?? "Flanking", uuid: token.actor.uuid });
        }
      }
    } else if (flankingMode === "midiFlanking") {
      const MidiFlanking = CONFIG.statusEffects.find((se) => se.id === "flanking");
      if (!MidiFlanking) return false;
      let cprEffect = getCPREffect(MidiFlanking?._id);
      if (!cprEffect) {
        if (!token.actor?.isOwner) await unTimedExecuteAsGM("toggleStatusEffect", { actorUuid: token.actor?.uuid, statusId: MidiFlanking.id, options: { active: needsFlanking } });
        else await token.actor.toggleStatusEffect(MidiFlanking.id, { active: needsFlanking });
      } else if (needsFlanking) {
        if (!token.actor?.effects.get(cprEffect.id)) {
          if (!token.actor?.isOwner) await unTimedExecuteAsGM("createEffects", { actorUuid: token.actor?.uuid, effects: [cprEffect.toObject()], options: { keepId: true } });
          else await token.actor.createEmbeddedDocuments("ActiveEffect", [cprEffect.toObject()], { keepId: true });
        }
      } else {
        if (token.actor?.effects.get(cprEffect.id)) {
          if (!target.actor.isOwner) await unTimedExecuteAsGM("removeEffects", { actorUuid: token.actor.uuid, effects: [cprEffect.id], options: {} });
          await token.actor.deleteEmbeddedDocuments("ActiveEffect", [cprEffect.id]);
        }
      }
    }
    return needsFlanking;
  } else if (isFlankedMode) {
    if (!target.actor) return false;
    const needsFlanked = await computeFlankedStatus(target);
    if (["ceflanked", "ceflankedNoconga"].includes(flankingMode)) {
      if (installedModules.get("dfreds-convenient-effects")) {
        let CEFlanked = getFlankedEffect();
        if (!CEFlanked) return false;
        const hasFlanked = CEHasEffectApplied({ effectName: CEFlanked.name ?? "Flanked", uuid: target.actor.uuid });
        if (needsFlanked && !hasFlanked && target.actor) {
          await CEAddEffectWith2({ effectName: CEFlanked.name ?? "Flanked", uuid: target.actor.uuid, overlay: false });
        } else if (!needsFlanked && hasFlanked && token?.actor) {
          await CERemoveEffect({ effectName: CEFlanked.name ?? "Flanked", uuid: target.actor.uuid });
        }
      }
    } else if (["midiFlanked", "midiFlankedNoConga"].includes(flankingMode)) {
      const MidiFlanked = CONFIG.statusEffects.find((se) => se.id === "flanked");
      if (!MidiFlanked) return false;
      const cprEffect = getCPREffect(MidiFlanked?._id);
      if (!cprEffect) {
        if (!target.actor.isOwner) await unTimedExecuteAsGM("toggleStatusEffect", { actorUuid: target.actor.uuid, statusId: MidiFlanked.id, options: { active: needsFlanked } });
        else await target.actor.toggleStatusEffect(MidiFlanked.id, { active: needsFlanked });
      } else if (needsFlanked) {
        if (!target.actor.effects.get(cprEffect.id)) {
          if (!target.actor.isOwner) await unTimedExecuteAsGM("createEffects", { actorUuid: target.actor.uuid, effects: [cprEffect.toObject()], options: { keepId: true } });
          else await target.actor.createEmbeddedDocuments("ActiveEffect", [cprEffect.toObject()], { keepId: true });
        }
      } else {
        if (target.actor.effects.get(cprEffect.id)) {
          if (!target.actor.isOwner) await unTimedExecuteAsGM("removeEffects", { actorUuid: target.actor.uuid, effects: [cprEffect.id], options: {} });
          else await target.actor.deleteEmbeddedDocuments("ActiveEffect", [cprEffect.id]);
        }
      }
    }
  }
  return false;
}
__name(markFlanking, "markFlanking");
async function chackFlanking(user, target, targeted) {
  if (user.id !== game.user?.id) return false;
  let token = canvas.tokens?.controlled[0];
  if (user.targets.size === 1) return markFlanking(token, target);
  return false;
}
__name(chackFlanking, "chackFlanking");
var flankingHighlightContainer = null;
function getFlankingHighlightIcon() {
  const flankingMode = checkRule("checkFlanking");
  const isFlankedMode = ["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(flankingMode);
  return isFlankedMode ? "modules/midi-qol/icons/encirclement.svg" : "icons/svg/sword.svg";
}
__name(getFlankingHighlightIcon, "getFlankingHighlightIcon");
async function updateFlankingHighlight() {
  if (!canvas?.grid || !canvas.interface?.grid) return;
  if (flankingHighlightContainer) {
    flankingHighlightContainer.destroy({ children: true });
    flankingHighlightContainer = null;
  }
  if (!configSettings.optionalRules?.showFlankingHighlight) return;
  const flankingMode = checkRule("checkFlanking");
  if (!flankingMode || flankingMode === "off") return;
  if (canvas.scene?.grid.type === CONST.GRID_TYPES.GRIDLESS) return;
  const token = canvas.tokens?.controlled?.[0];
  const target = game.user?.targets?.first();
  if (!token || !target || token === target) return;
  if (!canvas.dimensions) return;
  const gridDist = canvas.dimensions.distance ?? 5;
  let tokenRange = 1;
  if (token.actor?.items.contents.some((item) => item.system?.properties?.has("rch") && item.system.equipped)) {
    tokenRange = 2;
  }
  const tokenDist = getDistance(token, target, { wallsBlock: true });
  if (tokenDist > tokenRange * gridDist) return;
  const gridW = canvas.grid.sizeX ?? 100;
  const gridH = canvas.grid.sizeY ?? 100;
  let maxDist = gridDist;
  if (game.settings.get("core", "gridDiagonals") === 1) maxDist *= Math.sqrt(2);
  const targetOffsets = target.document.getOccupiedGridSpaceOffsets();
  const targetOffsetSet = new Set(targetOffsets.map((o) => `${o.i},${o.j}`));
  const targetCellCenters = targetOffsets.map((o) => canvas.grid.getCenterPoint(o));
  const bounds = {
    x: target.x - 2 * gridW,
    y: target.y - 2 * gridH,
    width: target.document.width * gridW + 4 * gridW,
    height: target.document.height * gridH + 4 * gridH
  };
  const [i0, j0, i1, j1] = canvas.grid.getOffsetRange(bounds);
  let top;
  let bottom;
  let left;
  let right;
  if (!canvas.grid.isHexagonal) {
    const tl = { x: target.x, y: target.y };
    const tr = { x: target.x + target.document.width * gridW, y: target.y };
    const bl = { x: target.x, y: target.y + target.document.height * gridH };
    const br = { x: target.x + target.document.width * gridW, y: target.y + target.document.height * gridH };
    top = [tl.x, tl.y, tr.x, tr.y];
    bottom = [bl.x, bl.y, br.x, br.y];
    left = [tl.x, tl.y, bl.x, bl.y];
    right = [tr.x, tr.y, br.x, br.y];
  }
  const tokenPoints = [];
  const tokenStartX = -Math.max(0, token.document.width / 2 - 0.5);
  const tokenStartY = -Math.max(0, token.document.height / 2 - 0.5);
  const tokenMaxDist = tokenRange * maxDist + 1;
  for (let x = tokenStartX; x < token.document.width / 2; x++) {
    for (let y = tokenStartY; y < token.document.height / 2; y++) {
      const tx = token.center.x + x * gridW;
      const ty = token.center.y + y * gridH;
      let closeEnough = false;
      for (const tc of targetCellCenters) {
        const d = canvas.grid.measurePath([{ x: tx, y: ty }, tc], {}).distance;
        if (d <= tokenMaxDist) {
          closeEnough = true;
          break;
        }
      }
      if (!closeEnough) continue;
      tokenPoints.push({ x: tx, y: ty });
    }
  }
  if (tokenPoints.length === 0) return;
  const centroid = canvas.grid.isHexagonal ? getHexCentroid(target) : void 0;
  const MIN_FLANKING_ANGLE = 150 / 180 * Math.PI;
  const validCells = [];
  for (let i = i0; i <= i1; i++) {
    for (let j = j0; j <= j1; j++) {
      if (targetOffsetSet.has(`${i},${j}`)) continue;
      const cellCenter = canvas.grid.getCenterPoint({ i, j });
      let isAdjacent = false;
      for (const tc of targetCellCenters) {
        const d = canvas.grid.measurePath([cellCenter, tc], {}).distance;
        if (d > 0 && d <= maxDist + 1) {
          isAdjacent = true;
          break;
        }
      }
      if (!isAdjacent) continue;
      let isValid = false;
      if (canvas.grid.isHexagonal && centroid) {
        for (const tp of tokenPoints) {
          const tokenCenter = canvas.grid.getCenterPoint(tp);
          const angleToken = Math.atan2(tokenCenter.y - centroid.y, tokenCenter.x - centroid.x);
          const angleCandidate = Math.atan2(cellCenter.y - centroid.y, cellCenter.x - centroid.x);
          let diff = Math.abs(angleToken - angleCandidate);
          if (diff > Math.PI) diff = 2 * Math.PI - diff;
          if (diff >= MIN_FLANKING_ANGLE) {
            isValid = true;
            break;
          }
        }
      } else if (top && bottom && left && right) {
        for (const tp of tokenPoints) {
          const p1 = canvas.grid.getCenterPoint(tp);
          const p2 = cellCenter;
          const ray = new foundry.canvas.geometry.Ray(p1, p2);
          const throughTopBottom = ray.intersectSegment(top) && ray.intersectSegment(bottom);
          const throughLeftRight = ray.intersectSegment(left) && ray.intersectSegment(right);
          if (throughTopBottom || throughLeftRight) {
            isValid = true;
            break;
          }
        }
      }
      if (isValid) validCells.push({ i, j });
    }
  }
  if (validCells.length === 0) return;
  const iconPath = getFlankingHighlightIcon();
  let texture;
  try {
    texture = await PIXI.Assets.load(iconPath);
  } catch {
    return;
  }
  if (!texture?.valid) return;
  if (!canvas?.interface?.grid) return;
  if (canvas.tokens?.controlled?.[0] !== token) return;
  if (game.user?.targets?.first() !== target) return;
  flankingHighlightContainer = new PIXI.Container();
  flankingHighlightContainer.name = "midi-qol-flanking-highlight";
  flankingHighlightContainer.zIndex = (CONFIG.Canvas.groups.interface.zIndexScrollingText ?? 1100) - 1;
  flankingHighlightContainer.sortableChildren = false;
  flankingHighlightContainer.eventMode = "none";
  canvas.interface.addChild(flankingHighlightContainer);
  const spriteSize = Math.min(gridW, gridH) * 0.8;
  for (const { i, j } of validCells) {
    const center = canvas.grid.getCenterPoint({ i, j });
    const sprite = new PIXI.Sprite(texture);
    sprite.anchor.set(0.5);
    sprite.position.set(center.x, center.y);
    sprite.width = spriteSize;
    sprite.height = spriteSize;
    sprite.alpha = 0.6;
    sprite.tint = 16737792;
    flankingHighlightContainer.addChild(sprite);
  }
}
__name(updateFlankingHighlight, "updateFlankingHighlight");
function getChanges2(actorOrItem, key) {
  let contents = actorOrItem.effects.contents;
  if (actorOrItem instanceof Actor) contents = actorOrItem.appliedEffects;
  return actorOrItem.effects.contents.flat().map((e) => {
    let c = foundry.utils.duplicate(e.changes);
    c = c.map((change) => {
      change.effect = e;
      return change;
    });
    return c;
  }).flat().filter((c) => c.key.includes(key)).sort((a, b) => a.key < b.key ? -1 : 1);
}
__name(getChanges2, "getChanges");
function canSense(tokenEntity, targetEntity, validModes = ["all"]) {
  return canSenseModes(tokenEntity, targetEntity, validModes).length > 0;
}
__name(canSense, "canSense");
function canSenseModes(tokenEntity, targetEntity, validModes = ["all"]) {
  const token = getToken(tokenEntity);
  const target = getToken(targetEntity);
  if (!token || !target) return [];
  return _canSenseModes(token, target, validModes);
}
__name(canSenseModes, "canSenseModes");
function initializeVision(tk, force = false) {
  tk.document.sight.enabled = true;
  tk.document._prepareDetectionModes();
  const sourceId = tk.sourceId;
  tk.vision = new CONFIG.Canvas.visionSourceClass({ sourceId, object: tk });
  tk.vision.initialize({
    x: tk.center.x,
    y: tk.center.y,
    elevation: tk.document.elevation,
    radius: Math.clamp(tk.sightRange, 0, canvas.dimensions?.maxR ?? 0),
    externalRadius: tk.externalRadius,
    // Math.max(tk.mesh.width, tk.mesh.height) / 2,
    angle: tk.document.sight.angle,
    contrast: tk.document.sight.contrast,
    saturation: tk.document.sight.saturation,
    brightness: tk.document.sight.brightness,
    attenuation: tk.document.sight.attenuation,
    rotation: tk.document.rotation,
    visionMode: tk.document.sight.visionMode,
    preview: !!tk._original,
    color: tk.document.sight.color?.toNearest(),
    blinded: tk.document.hasStatusEffect(CONFIG.specialStatusEffects.BLIND)
  });
  if (!tk.vision.los) {
    tk.vision.shape = tk.vision._createRestrictedPolygon();
    tk.vision.los = tk.vision.shape;
  }
  if (tk.vision.visionMode) tk.vision.visionMode.animated = false;
  tk.vision.add();
  return true;
}
__name(initializeVision, "initializeVision");
function _canSenseModes(token, target, validModesParam = ["all"]) {
  const detectionModes = CONFIG.Canvas.detectionModes;
  const DetectionModeCONST = foundry.canvas.perception.DetectionMode;
  if (!token || !target) return ["noToken"];
  if (target.document.hidden || token.document.hidden) return [];
  if (!token.hasSight && !configSettings.optionalRules.invisVision) return ["senseAll"];
  if ((!token.vision || !token.vision.los) && !initializeVision(token)) return ["noSight"];
  const matchedModes = /* @__PURE__ */ new Set();
  const t = Math.min(target.w, target.h) / 4;
  const t2 = Math.min(target.w, target.h) / 2 - 2;
  const targetPoint = target.center;
  const offsets = t > 0 ? [
    [0, 0],
    [-t, -t],
    [-t, t],
    [t, t],
    [t, -t],
    [-t, 0],
    [t, 0],
    [0, -t],
    [0, t],
    [-t2, -t2],
    [-t2, t2],
    [t2, t2],
    [t2, -t2],
    [-t2, 0],
    [t2, 0],
    [0, -t2],
    [0, t2]
  ] : [[0, 0]];
  const tests = offsets.map((o) => ({
    point: new PIXI.Point(targetPoint.x + o[0], targetPoint.y + o[1]),
    elevation: target?.document.elevation ?? 0,
    los: /* @__PURE__ */ new Map()
  }));
  const config = { tests, object: target };
  const tokenDetectionModes = token.detectionModes;
  const modes = CONFIG.Canvas.detectionModes;
  let validModes = new Set(validModesParam);
  if (token.vision) {
    const origin = { x: token.vision.x, y: token.vision.y };
    for (const test of tests) {
      const collision = CONFIG.Canvas.polygonBackends.sight.testCollision(
        origin,
        test.point,
        { type: "sight", mode: "any", source: token.vision, useThreshold: true, priority: token.vision.priority }
      );
      test.los.set(token.vision, !collision);
    }
  }
  const lightSources = canvas.effects?.lightSources;
  for (const lightSource of lightSources ?? []) {
    if (
      /*!lightSource.data.vision ||*/
      !lightSource.active || lightSource.data.disabled
    ) continue;
    if (!validModes.has(detectionModes.lightPerception?.id ?? DetectionModeCONST.BASIC_MODE_ID) && !validModes.has("all")) continue;
    const result = lightSource.testVisibility && lightSource.testVisibility(config);
    if (result === true) matchedModes.add(detectionModes.lightPerception?.id ?? DetectionModeCONST.BASIC_MODE_ID);
  }
  const lightPerception = tokenDetectionModes.find((m) => m.id === modes.lightPerception?.id);
  if (lightPerception && ["lightPerception", "all"].some((mode) => validModes.has(mode))) {
    const result = lightPerception && token.vision ? modes.lightPerception.testVisibility(token.vision, lightPerception, config) : false;
    if (result === true)
      matchedModes.add(detectionModes.lightPerception?.id ?? DetectionModeCONST.BASIC_MODE_ID);
  }
  const basic = tokenDetectionModes.find((m) => m.id === DetectionModeCONST.BASIC_MODE_ID);
  if (basic && ["basicSight", "all"].some((mode) => validModes.has(mode))) {
    const result = token.vision ? modes.basicSight.testVisibility(token.vision, basic, config) : false;
    if (result === true)
      matchedModes.add(detectionModes.basicSight?.id ?? DetectionModeCONST.BASIC_MODE_ID);
  }
  for (const detectionMode of tokenDetectionModes) {
    if (detectionMode.id === DetectionModeCONST.BASIC_MODE_ID) continue;
    if (!detectionMode.enabled) continue;
    const dm = modes[detectionMode.id];
    if (validModes.has("all") || validModes.has(detectionMode.id)) {
      const result = token.vision ? dm?.testVisibility(token.vision, detectionMode, config) : false;
      if (result === true) {
        matchedModes.add(detectionMode.id);
      }
    }
  }
  for (let tk of [token, target]) {
    if (!tk.document.sight.enabled) {
      const sourceId = tk.sourceId;
      canvas.effects?.visionSources.delete(sourceId);
    }
  }
  return Array.from(matchedModes);
}
__name(_canSenseModes, "_canSenseModes");
function tokensForActor(actorRef) {
  let actor;
  if (!actorRef) return [];
  if (typeof actorRef === "string") actor = fromActorUuid(actorRef);
  else actor = actorRef;
  if (!(actor instanceof Actor)) return [];
  if (actor.token) return [actor.token.object].filter((t) => !!t);
  const tokens = actor.getActiveTokens();
  if (!tokens.length) return [];
  const controlled = tokens.filter((t) => t.controlled);
  return controlled.length ? controlled : tokens;
}
__name(tokensForActor, "tokensForActor");
function tokenForActor(actor) {
  const tokens = tokensForActor(actor);
  return tokens[0];
}
__name(tokenForActor, "tokenForActor");
async function doConcentrationCheck(actor, saveDC) {
  const concentratingOnItemNames = Array.from(actor.concentration.items).map((item) => item.name);
  if (!concentratingOnItemNames.length) concentratingOnItemNames.push("No item");
  const itemDisplayName = `${concentrationCheckItemDisplayName}: ${concentratingOnItemNames.join(", ")}`;
  const itemData = {
    name: itemDisplayName,
    // @ts-expect-error no dnd5e-types
    type: "feat",
    img: "./modules/midi-qol/icons/concentrate.png",
    system: {
      activities: {
        concentrationCheck: {
          type: "save",
          activation: {
            type: "special"
          },
          target: {
            affects: {
              choice: false,
              count: "",
              type: "self"
            },
            override: true,
            prompt: false
          },
          damage: {
            parts: [],
            onSave: "half"
          },
          save: {
            // @ts-expect-error no dnd5e-types
            ability: actor.system.attributes.concentration.ability || "con",
            dc: {
              calculation: "",
              formula: `${saveDC}`
            }
          },
          useConditionText: "",
          forceDialog: false,
          effectConditionText: ""
        }
      },
      identifier: "concentration-check-midi-qol"
    },
    flags: {
      "midi-qol": {
        onUseMacroName: "[postActiveEffects]ItemMacro",
        isConcentrationCheck: true,
        noProvokeReaction: true
      },
      dae: {
        macro: {
          _id: null,
          name: "Concentration Check - Midi QOL",
          type: "script",
          author: "devnIbfBHb74U9Zv",
          img: "icons/svg/dice-target.svg",
          scope: "global",
          command: `
            if (MidiQOL.configSettings().autoCheckSaves === 'none') return;
            for (let targetUuid of args[0].targetUuids) {
              let target = await fromUuid(targetUuid);
              if (MidiQOL.configSettings().removeConcentration && (target.actor.system.attributes.hp.value === 0 || args[0].failedSaveUuids.find(uuid => uuid === targetUuid))) {
                await target.actor.endConcentration();
              }
            }`,
          folder: null,
          sort: 0,
          flags: {}
        }
      }
    }
  };
  return await _doConcentrationCheck(actor, itemData);
}
__name(doConcentrationCheck, "doConcentrationCheck");
async function _doConcentrationCheck(actor, itemData) {
  let result;
  foundry.utils.setProperty(itemData, `flags.${MODULE_ID}.syntheticItem`, true);
  let ownedItem = new CONFIG.Item.documentClass(itemData, { parent: actor });
  ownedItem.prepareData();
  ownedItem.prepareFinalAttributes();
  try {
    const midiOptions = { checkGMStatus: true, isConcentrationCheck: true, createWorkflow: true, workflowOptions: { versatile: false, targetConfirmation: "none" } };
    result = await completeItemUse(ownedItem, { midiOptions }, { configure: false }, { systemCard: false });
  } catch (err) {
    const message = "midi-qol | doConcentrationCheck";
    TroubleShooter.recordError(err, message);
    console.warn(message, err);
  } finally {
    return result;
  }
}
__name(_doConcentrationCheck, "_doConcentrationCheck");
function hasDAE(workflow) {
  return installedModules.get("dae") && (workflow.item.effects?.some((ef) => ef?.transfer === false) || workflow.ammunition?.effects?.some((ef) => ef?.transfer === false));
}
__name(hasDAE, "hasDAE");
async function displayDSNForRoll(rolls, rollType, defaultRollMode) {
  if (!rolls) return;
  if (!(rolls instanceof Array)) rolls = [rolls];
  const promises = [];
  for (let roll of rolls) {
    if (dice3dEnabled()) {
      const dice3d = game.dice3d;
      let ghostRoll = false;
      let whisperUsers = null;
      const rollMode = defaultRollMode || game.settings.get("core", "rollMode");
      let hideRoll = false;
      if (game.user?.isGM) {
        let dsnSetting = "show";
        switch (rollType) {
          case "attackRollD20":
          case "attackRoll":
            dsnSetting = configSettings.gmAttackDsn ?? "show";
            break;
          case "damageRoll":
            dsnSetting = configSettings.gmDamageDsn ?? "show";
            break;
          default:
            dsnSetting = "show";
            break;
        }
        if (dsnSetting === "none") {
          hideRoll = true;
        } else if (dsnSetting === "ghost") {
          ghostRoll = true;
        } else if (rollType === "attackRollD20") {
          for (let i = 1; i < roll.dice.length; i++) {
            roll.dice[i].results.forEach((r) => foundry.utils.setProperty(r, "hidden", true));
          }
        }
      }
      if (!ghostRoll) {
        ghostRoll = rollMode === "blindroll";
      }
      if (rollMode === "selfroll" || rollMode === "gmroll" || rollMode === "blindroll") {
        whisperUsers = ChatMessage.getWhisperRecipients("GM");
        if (rollMode !== "blindroll" && game.user) whisperUsers.concat(game.user);
      }
      if (!hideRoll) {
        let displayRoll = Roll.fromData(JSON.parse(JSON.stringify(roll)));
        if (game.user?.isGM && configSettings.addFakeDice) {
          for (let term of displayRoll.terms) {
            if (term instanceof foundry.dice.terms.Die) {
              if (["attackRoll", "attackRollD20"].includes(rollType ?? "") && term.faces === 20 && term.number !== 1) continue;
              let numExtra = Math.ceil((term.number ?? 1) * Math.random());
              let extraDice = await new foundry.dice.terms.Die({ faces: term.faces, number: numExtra }).evaluate();
              term.number = (term.number ?? 1) + numExtra;
              term.results = term.results.concat(extraDice.results);
            }
          }
        }
        displayRoll.terms.forEach((term) => {
          if (term.options?.flavor) term.options.flavor = term.options.flavor.toLocaleLowerCase();
          else term.options.flavor = displayRoll.options.type;
        });
        if (ghostRoll) {
          promises.push(dice3d?.showForRoll(displayRoll, game.user, true, ChatMessage.getWhisperRecipients("GM"), !game.user?.isGM));
          if (game.settings.get("dice-so-nice", "showGhostDice")) {
            displayRoll.ghost = true;
            promises.push(dice3d?.showForRoll(displayRoll, game.user, true, game.users?.players.map((u) => u.id), game.user?.isGM));
          }
        } else
          promises.push(dice3d?.showForRoll(displayRoll, game.user, true, whisperUsers, rollMode === "blindroll" && !game.user?.isGM));
      }
    }
  }
  if (promises.length) await Promise.all(promises);
  DSNMarkDiceDisplayed(rolls);
}
__name(displayDSNForRoll, "displayDSNForRoll");
function DSNMarkDiceDisplayed(rolls) {
  if (rolls instanceof Roll) rolls = [rolls];
  for (let roll of rolls)
    roll.dice.forEach((d) => d.results.forEach((r) => foundry.utils.setProperty(r, "hidden", true)));
}
__name(DSNMarkDiceDisplayed, "DSNMarkDiceDisplayed");
function isReactionItem(item) {
  if (!item) return false;
  return item.system.activities.some((activity) => activity.activation?.type?.includes("reaction"));
}
__name(isReactionItem, "isReactionItem");
function getCriticalDamage() {
  return game.user?.isGM ? criticalDamageGM : criticalDamage;
}
__name(getCriticalDamage, "getCriticalDamage");
function isValidTarget(target) {
  if (!target.actor) return false;
  if (target.actor.type === "group") return false;
  if (target.actor.getFlag(MODULE_ID, "neverTarget")) return false;
  if (target instanceof TokenDocument) {
    if (target.isSecret) return false;
    if (target.hidden) return false;
  } else {
    if (target.document.isSecret) return false;
    if (target.document.hidden) return false;
  }
  return true;
}
__name(isValidTarget, "isValidTarget");
function validRollAbility(rollType, ability) {
  if (typeof ability !== "string") return void 0;
  ability = ability.toLocaleLowerCase().trim();
  switch (rollType) {
    case "test":
    case "abil":
    case "save":
      if (GameSystemConfig.abilities[ability]) return ability;
      return Object.keys(GameSystemConfig.abilities).find((abl) => GameSystemConfig.abilities[abl].label.toLocaleLowerCase() === ability.trim().toLocaleLowerCase());
    case "skill":
      if (GameSystemConfig.skills[ability]) return ability;
      return Object.keys(GameSystemConfig.skills).find((skl) => GameSystemConfig.skills[skl].label.toLocaleLowerCase() === ability.trim().toLocaleLowerCase());
    default:
      return void 0;
  }
}
__name(validRollAbility, "validRollAbility");
async function contestedRoll(data) {
  const source = data.source;
  const target = data.target;
  const sourceToken = getToken(source?.token);
  const targetToken = getToken(target?.token);
  const { rollOptions, success, failure, drawn, displayResults, itemCardUuid, flavor } = data;
  let canProceed = true;
  if (!source || !target || !sourceToken || !targetToken || !source.rollType || !target.rollType || !source.ability || !target.ability || !validRollAbility(source.rollType, source.ability) || !validRollAbility(target.rollType, target.ability)) {
    error2(`contestRoll | source[${sourceToken?.name}], target[${targetToken?.name}], source.rollType[${source.rollType}], target.rollType[${target?.rollType}], source.ability[${source.ability}], target.ability[${target?.ability}] must all be defined`);
    canProceed = false;
  }
  if (!["test", "abil", "save", "skill"].includes(source?.rollType ?? "")) {
    error2(`contestedRoll | sourceRollType must be one of test/abil/skill/save not ${source.rollType}`);
    canProceed = false;
  }
  if (!["test", "abil", "save", "skill"].includes(target?.rollType ?? "")) {
    error2(`contestedRoll | target.rollType must be one of test/abil/skill/save not ${target.rollType}`);
    canProceed = false;
  }
  if (!canProceed) return { result: void 0, rolls: [] };
  const sourceDocument = sourceToken.document;
  const targetDocument = targetToken.document;
  source.ability = validRollAbility(source.rollType, source.ability) ?? "";
  target.ability = validRollAbility(target.rollType, target.ability) ?? "";
  let player1 = playerFor(sourceToken);
  if (!player1?.active) player1 = preferredActiveGM();
  let player2 = playerFor(targetToken);
  if (!player2?.active) player2 = preferredActiveGM();
  if (!player1 || !player2) return { result: void 0, rolls: [] };
  const normalizeRollType = /* @__PURE__ */ __name((rt) => {
    if (rt === "abil" || rt === "test") return "check";
    return rt.trim();
  }, "normalizeRollType");
  const buildContestedSaveDetails = /* @__PURE__ */ __name((actorUuid, rollType, abilityOrSkill) => {
    const normalizedType = normalizeRollType(rollType);
    const isSkill = rollType === "skill";
    return {
      actorUuid,
      advantage: void 0,
      disadvantage: void 0,
      modifierTracker: void 0,
      isFriendly: false,
      isMagicSave: false,
      isConcentrationCheck: void 0,
      rollDC: 0,
      saveItemUuid: "",
      workflowId: "",
      itemCardUuid: itemCardUuid ?? "",
      workflowOptions: {},
      rollType: normalizedType,
      rollAbilities: isSkill ? [] : [abilityOrSkill.trim()],
      rollSkills: isSkill ? [abilityOrSkill.trim()] : [],
      rollTools: [],
      itemId: ""
    };
  }, "buildContestedSaveDetails");
  const sourceSaveDetails = buildContestedSaveDetails(
    sourceDocument?.actor?.uuid ?? "",
    source.rollType,
    source.ability
  );
  const targetSaveDetails = buildContestedSaveDetails(
    targetDocument?.actor?.uuid ?? "",
    target.rollType,
    target.ability
  );
  const resultPromises = [
    socketlibSocket.executeAsUser("rollAbility", player1.id, {
      saveDetails: sourceSaveDetails,
      displayOptions: { chatMessage: true }
    }),
    socketlibSocket.executeAsUser("rollAbility", player2.id, {
      saveDetails: targetSaveDetails,
      displayOptions: { chatMessage: true }
    })
  ];
  let results = await Promise.all(resultPromises);
  let roll1 = results[0];
  let roll2 = results[1];
  if (roll1 instanceof Array) roll1 = roll1[0];
  if (roll2 instanceof Array) roll2 = roll2[0];
  let result = roll1.total - roll2.total;
  if (isNaN(result)) result = void 0;
  if (displayResults !== false) {
    let resultString;
    if (result === void 0) resultString = "";
    else resultString = result > 0 ? i18n("midi-qol.save-success") : result < 0 ? i18n("midi-qol.save-failure") : result === 0 ? i18n("midi-qol.save-drawn") : "no result";
    const skippedString = i18n("midi-qol.Skipped");
    const content = `${flavor ?? i18n("midi-qol.ContestedRoll")} ${resultString} ${roll1.total ?? skippedString} ${i18n("midi-qol.versus")} ${roll2.total ?? skippedString}`;
    displayContestedResults(itemCardUuid, content, ChatMessage.getSpeaker({ token: sourceToken }), flavor);
  }
  const rollsToReturn = [roll1, roll2];
  if (result === void 0) return { result, rolls: rollsToReturn };
  if (result > 0 && success) success(rollsToReturn);
  else if (result < 0 && failure) failure(rollsToReturn);
  else if (result === 0 && drawn) drawn(rollsToReturn);
  return { result, rolls: rollsToReturn };
}
__name(contestedRoll, "contestedRoll");
function displayContestedResults(chatCardUuid, resultContent, speaker, flavor) {
  let itemCard = getCachedDocument2(chatCardUuid) ?? fromUuidSync(chatCardUuid);
  if (itemCard) {
    let content = foundry.utils.duplicate(itemCard.content ?? "");
    const searchRE = /<div class="midi-qol-saves-display">[\s\S]*?<div class="end-midi-qol-saves-display">/;
    const replaceString = `<div class="midi-qol-saves-display">${resultContent}<div class="end-midi-qol-saves-display">`;
    content = content.replace(searchRE, replaceString);
    itemCard.update({ content });
  } else {
    ChatMessage.create({ content: `<p>${resultContent}</p>`, speaker });
  }
}
__name(displayContestedResults, "displayContestedResults");
function hasWallBlockingCondition(target) {
  return globalThis.MidiQOL.WallsBlockConditions.some((cond) => hasCondition(target.actor, cond));
}
__name(hasWallBlockingCondition, "hasWallBlockingCondition");
function getActor(actorRef) {
  if (!actorRef) return null;
  const entity = typeof actorRef === "string" ? fromUuidSync(actorRef) : actorRef;
  if (entity instanceof Actor) return entity;
  if (entity instanceof Token2) return entity.actor;
  if (entity instanceof TokenDocument) return entity.actor;
  if (entity instanceof Item && entity.parent instanceof Actor) return entity.parent;
  if (entity instanceof ActiveEffect && entity.parent instanceof Actor) return entity.parent;
  if (entity instanceof ActiveEffect && entity.parent instanceof Item && entity.parent.parent instanceof Actor) return entity.parent.parent;
  return null;
}
__name(getActor, "getActor");
function getTokenDocument(tokenRef) {
  if (!tokenRef) return void 0;
  const entity = typeof tokenRef === "string" ? fromUuidSync(tokenRef) : tokenRef;
  if (entity instanceof TokenDocument) return entity;
  if (entity instanceof Token2) return entity.document;
  if (entity instanceof Actor) return tokenForActor(entity)?.document;
  return void 0;
}
__name(getTokenDocument, "getTokenDocument");
function getToken(tokenRef) {
  if (!tokenRef) return void 0;
  const entity = typeof tokenRef === "string" ? fromUuidSync(tokenRef) : tokenRef;
  if (entity instanceof Token2) return entity;
  if (entity instanceof TokenDocument) return entity.object ?? void 0;
  if (entity instanceof Actor) return tokenForActor(entity);
  if (entity instanceof Item && entity.parent instanceof Actor) return tokenForActor(entity.parent);
  if (entity instanceof ActiveEffect && entity.parent instanceof Actor) return tokenForActor(entity.parent);
  if (entity instanceof ActiveEffect && entity.parent instanceof Item && entity.parent.parent instanceof Actor) return tokenForActor(entity.parent?.parent);
  return void 0;
}
__name(getToken, "getToken");
function getPlaceable(tokenRef) {
  if (!tokenRef) return void 0;
  const entity = typeof tokenRef === "string" ? fromUuidSync(tokenRef) : tokenRef;
  if (entity instanceof foundry.canvas.placeables.PlaceableObject) return entity;
  if (entity instanceof Actor) return tokenForActor(entity);
  if (entity instanceof Item && entity.parent instanceof Actor) return tokenForActor(entity.parent);
  if (entity instanceof ActiveEffect && entity.parent instanceof Actor) return tokenForActor(entity.parent);
  if (entity instanceof ActiveEffect && entity.parent instanceof Item && entity.parent.parent instanceof Actor) return tokenForActor(entity.parent?.parent);
  if (entity instanceof TokenDocument) return entity.object ?? void 0;
  return void 0;
}
__name(getPlaceable, "getPlaceable");
function calcTokenCover(attacker, target) {
  const attackerToken = getToken(attacker);
  const targetToken = getToken(target);
  const coverCalc = attackerToken?.coverCalculator;
  if (!attackerToken || !targetToken || !coverCalc) {
    let message = "midi-qol | calcTokenCover | failed";
    if (!coverCalc)
      message += " tokencover not installed or cover calculator not found";
    if (!attackerToken)
      message += " atacker token not valid";
    if (!targetToken)
      message += " target token not valid";
    const err = new Error("calcTokenCover failed");
    TroubleShooter.recordError(err, message);
    console.warn(message, err);
    return 0;
  }
  let targetCover = coverCalc.targetCover(target);
  return targetCover;
}
__name(calcTokenCover, "calcTokenCover");
function getTokenName(entity) {
  const token = getToken(entity);
  if (!token) return "<unknown>";
  return getTokenNameExact(token) ?? "<unkown>";
}
__name(getTokenName, "getTokenName");
function getTokenNameExact(entity) {
  if (!(entity instanceof Token2)) return void 0;
  let name;
  if (configSettings.useTokenNames) name = entity.name ?? entity.actor?.name ?? "<unknown>";
  else name = entity.actor?.name ?? entity.name ?? "<unknown>";
  const suffix = safeGetGameSetting("hide-npc-names", "tokenHiddenSuffix");
  if (suffix) name = name.replace(` ${suffix}`, "");
  return name;
}
__name(getTokenNameExact, "getTokenNameExact");
function createSquareGridProxy() {
  if (!canvas.grid) throw new Error("createSquareGridProxy called without canvas.grid");
  const diagonals = safeGetGameSetting("core", "gridDiagonals");
  const canvasGridProxy = new Proxy(canvas.grid, {
    get: /* @__PURE__ */ __name(function(target, prop, _receiver) {
      if (foundry.grid.SquareGrid.prototype[prop] instanceof Function) {
        return foundry.grid.SquareGrid.prototype[prop].bind(canvasGridProxy);
      } else if (prop === "diagonals") {
        return diagonals;
      } else if (prop === "isSquare") return true;
      else if (prop === "isGridless") return false;
      else if (prop === "isHex") return false;
      return Reflect.get(target, prop);
    }, "get")
  });
  return canvasGridProxy;
}
__name(createSquareGridProxy, "createSquareGridProxy");
function midiMeasureDistances(segments, options = {}) {
  let isGridless = canvas.grid?.constructor.name === "GridlessGrid";
  if (!isGridless || !options.gridSpaces || !configSettings.griddedGridless || !canvas.grid) {
    return segments.map((s) => canvas.grid?.measurePath([s.ray.A, s.ray.B], {})).map((d) => d?.distance ?? 0);
  }
  if (!canvas.grid) return [0];
  const diagonals = safeGetGameSetting("core", "gridDiagonals");
  const canvasGridProxy = createSquareGridProxy();
  const GridDiagonals = CONST.GRID_DIAGONALS;
  if ([GridDiagonals.APPROXIMATE, GridDiagonals.EQUIDISTANT, GridDiagonals.ALTERNATING_1, GridDiagonals.ALTERNATING_2].includes(diagonals)) {
    segments = segments.map((s) => {
      const gridPosA = canvasGridProxy.getOffset(s.ray.A);
      const aCenter = canvasGridProxy.getCenterPoint(gridPosA);
      const gridPosB = canvasGridProxy.getOffset(s.ray.B);
      const bCenter = canvasGridProxy.getCenterPoint(gridPosB);
      return { ray: new foundry.canvas.geometry.Ray(aCenter, bCenter) };
    });
  }
  let distances = segments.map((s) => canvasGridProxy.measurePath([s.ray.A, s.ray.B], {}));
  return distances.map((d) => {
    let distance = d.distance;
    let fudgeFactor = configSettings.gridlessFudge ?? 0;
    switch (diagonals) {
      case GridDiagonals.EQUIDISTANT:
      case GridDiagonals.ALTERNATING_1:
      case GridDiagonals.ALTERNATING_2:
        break;
      case GridDiagonals.EXACT:
      case GridDiagonals.RECTILINEAR:
        if (d.diagonals > 0)
          distance = Math.max(0, d.distance - Math.SQRT2 * fudgeFactor);
        else distance = Math.max(0, d.distance - fudgeFactor);
        break;
      case GridDiagonals.APPROXIMATE:
        if (d.diagonals > 0)
          distance = Math.max(0, d.distance - fudgeFactor);
        break;
      case GridDiagonals.ILLEGAL:
      default:
        distance = d.distance;
    }
    return distance;
  });
}
__name(midiMeasureDistances, "midiMeasureDistances");
function getActivityAutoTargetAction2(activity) {
  const item = activity?.item;
  if (!item) return configSettings.autoTarget;
  const autoTarget = activity.midiProperties?.autoTargetAction;
  if (!autoTarget || autoTarget === "default") return configSettings.autoTarget;
  return autoTarget;
}
__name(getActivityAutoTargetAction2, "getActivityAutoTargetAction");
function getAoETargetType2(activity) {
  let AoETargetType = "any";
  const activityTarget = activity.target;
  if (activityTarget) {
    if (activityTarget.affects.type === "ally") AoETargetType = "ally";
    if (activityTarget.affects.type === "enemy") AoETargetType = "enemy";
    if (activityTarget.affects.type === "creature") AoETargetType = "any";
  }
  if (activity.midiProperties?.autoTargetType !== "any") {
    AoETargetType = activity.midiProperties?.autoTargetType ?? "";
  }
  return AoETargetType;
}
__name(getAoETargetType2, "getAoETargetType");
function activityHasAutoPlaceTemplate(activity) {
  return activity && ["self", void 0].includes(activity.range?.units) && ["radius", "squareRadius"].includes(activity.target?.template.type);
}
__name(activityHasAutoPlaceTemplate, "activityHasAutoPlaceTemplate");
function activityHasEmanationNoTemplate(activity) {
  return activity && activity.target?.template.type === "emanationNoTemplate";
}
__name(activityHasEmanationNoTemplate, "activityHasEmanationNoTemplate");
function addRollTo(roll, bonusRoll) {
  const OperatorTerm = foundry.dice.terms.OperatorTerm;
  if (!bonusRoll) return roll;
  if (!roll) return bonusRoll;
  if (!roll._evaluated) roll = roll.clone().evaluate({ async: false });
  else {
    for (let term of roll.terms) {
      if (!term._evaluated && term instanceof OperatorTerm) {
        term.evaluate();
      }
    }
  }
  if (!bonusRoll._evaluated) bonusRoll = bonusRoll.clone().evaluate({ async: false });
  let terms;
  for (let term of bonusRoll.terms) {
    if (!term._evaluated && term instanceof OperatorTerm) {
      term.evaluate();
    }
  }
  if (bonusRoll.terms[0] instanceof OperatorTerm) {
    terms = roll.terms.concat(bonusRoll.terms);
  } else {
    const operatorTerm = new OperatorTerm({ operator: "+" });
    terms = roll.terms.concat([operatorTerm]);
    terms = terms.concat(bonusRoll.terms);
  }
  let newRoll = roll.constructor.fromTerms(terms);
  newRoll.resetFormula();
  newRoll.options = roll.options;
  return newRoll;
}
__name(addRollTo, "addRollTo");
async function chooseEffect({ actor, token, item, workflow, options }) {
  if (!item) return false;
  const effects = item.effects.filter(
    (e) => !e.transfer && !!(options?.chooseAll || e.flags?.dae?.dontApply)
  );
  if (effects.length === 0) {
    if (debugEnabled > 0)
      warn(`chooseEffect | no effects found for ${item.name}`);
    return false;
  }
  let targets = workflow.effectTargets;
  let origin = effects[0].uuid;
  if (workflow?.chatCard.getFlag("dnd5e", "use.concentrationId")) {
    origin = workflow.actor.effects.get(workflow.chatCard.getFlag("dnd5e", "use.concentrationId") ?? "")?.uuid ?? item?.uuid;
  }
  if (!targets || targets.size === 0) return;
  const { DialogV2: DialogV26 } = foundry.applications.api;
  const timeout = options?.timeout ?? configSettings.reactionTimeout ?? defaultTimeout;
  let timeRemaining = timeout;
  let second1TimeoutId;
  let timeoutId;
  const titleBase = `${i18n("CONTROLS.CommonSelect")} ${i18n("DOCUMENT.ActiveEffect")}`;
  const targetContent = `<p style="text-align:center"><b>${i18n("EFFECT.StatusTarget")}: [</b>${[...targets].map((t) => t.name)}<b>]</b></p>`;
  const buttons = effects.map((effect) => ({
    action: effect.uuid,
    label: effect.name,
    callback: /* @__PURE__ */ __name(() => effect, "callback")
  }));
  const chosenEffect = await DialogV26.wait({
    window: { title: `${titleBase}: ${timeRemaining}s` },
    classes: ["midi-qol-choose-effect"],
    position: { width: 220 },
    content: targetContent,
    buttons,
    render: /* @__PURE__ */ __name((_event, dialog) => {
      dialog.element.querySelectorAll(".form-footer button").forEach((btn) => {
        const eff = fromUuidSync(btn.dataset.action ?? "");
        if (!eff) return;
        const img = document.createElement("img");
        img.src = eff.img;
        const span = btn.querySelector("span");
        if (span) span.dataset.tooltip = eff.name;
        btn.prepend(img);
      });
      const titleEl = dialog.element.querySelector(".window-title");
      const set1SecondTimeout = /* @__PURE__ */ __name(function() {
        second1TimeoutId = setTimeout(() => {
          if (!timeoutId) return;
          timeRemaining -= 1;
          if (titleEl) titleEl.textContent = `${titleBase}: ${timeRemaining}s`;
          if (timeRemaining > 0) set1SecondTimeout();
        }, 1e3);
      }, "set1SecondTimeout");
      timeoutId = setTimeout(() => {
        if (debugEnabled > 0) warn(`chooseEffect | timeout fired closing dialog`);
        clearTimeout(second1TimeoutId);
        dialog.close();
      }, timeout * 1e3);
      set1SecondTimeout();
    }, "render"),
    close: /* @__PURE__ */ __name(() => {
      clearTimeout(timeoutId);
      clearTimeout(second1TimeoutId);
    }, "close")
  });
  if (!chosenEffect || !(chosenEffect instanceof ActiveEffect)) return false;
  const effectData = chosenEffect.toObject();
  effectData.origin = item.uuid;
  foundry.utils.setProperty(effectData, "flags.dae.dontApply", false);
  const applyItem = item.clone({ effects: [effectData] }, { keepId: true });
  await globalThis.DAE.doEffects(
    applyItem,
    true,
    targets,
    {
      damageTotal: 0,
      origin,
      critical: false,
      fumble: false,
      itemCardUuid: "",
      metaData: {},
      selfEffects: "none",
      // @ts-expect-error no dnd5e-types
      spellLevel: workflow?.spellLevel ?? applyItem.level ?? 0,
      toggleEffect: workflow?.activity.midiProperties?.toggleEffect,
      tokenUuid: token.document.uuid,
      actorUuid: actor.uuid,
      whisper: false,
      workflowOptions: workflow?.workflowOptions,
      context: {}
    }
  );
  if (chosenEffect.toObject()) {
    if (debugEnabled)
      warn(
        `chooseEffect | applying effect ${chosenEffect.name} to ${targets.size} targets`,
        targets
      );
  }
  return chosenEffect;
}
__name(chooseEffect, "chooseEffect");
function canSee(tokenEntity, targetEntity) {
  const NON_SIGHT_CONSIDERED_SIGHT = ["blindsight"];
  const detectionModes = CONFIG.Canvas.detectionModes;
  const sightDetectionModes = Object.keys(detectionModes).filter(
    (d) => detectionModes[d].type === foundry.canvas.perception.DetectionMode.DETECTION_TYPES.SIGHT || NON_SIGHT_CONSIDERED_SIGHT.includes(d)
  );
  return canSense(tokenEntity, targetEntity, sightDetectionModes);
}
__name(canSee, "canSee");
function sumRolls(rolls = [], countHealing) {
  if (!rolls) return 0;
  if (countHealing === void 0) countHealing = "positive";
  return rolls.reduce((total, roll) => {
    const type = roll.options.type;
    if (type === "midi-none") return total;
    if (["temphp"].includes(type) && countHealing === "negativeIgnoreTemp") return total;
    if (["temphp"].includes(type) && countHealing === "positiveIgnoreTemp") return total;
    if (["temphp", "healing"].includes(type) && countHealing === "ignore") return total;
    if (["temphp", "healing"].includes(type) && countHealing?.startsWith("negative")) return total - (roll?.total ?? 0);
    return total + (roll?.total ?? 0);
  }, 0);
}
__name(sumRolls, "sumRolls");
function getThrottlingFunction() {
  return foundry.utils.throttle(_updateAction, debounceInterval);
}
__name(getThrottlingFunction, "getThrottlingFunction");
var updatesCache = {};
async function _updateAction(document2, updates) {
  if (!updates) {
    if (debugEnabled > 0) warn(`_updateAction | No updates found for ${document2.uuid}`);
    return document2;
  }
  const baseDocument = fromUuidSync(document2.uuid);
  if (!baseDocument) {
    console.warn(`midi-qol | _updateAction | baseDocument not found for ${document2.uuid}`);
    return document2;
  }
  const updateDelay = 0;
  if (debugEnabled) warn("_updateAction | Starting for", Date.now(), updates);
  delete updates._id;
  if (updateDelay) {
    await busyWait(updateDelay);
  }
  await baseDocument?.update(updates);
  if (updateDelay) {
    await busyWait(updateDelay);
  }
  updatesCache[baseDocument.uuid] = foundry.utils.diffObject(updates, updatesCache[baseDocument.uuid] ?? {});
  if (debugEnabled > 1 && updates.content && updatesCache[baseDocument.uuid]?.content)
    warn("_updateAction | queued content differences ", diffStringsFull(updates.content, updatesCache[baseDocument.uuid].content, { ignoreWhitespace: true, ignoreNewlines: true }));
  if (debugEnabled) warn("_updateAction | post update updatesCache", Date.now(), updates);
}
__name(_updateAction, "_updateAction");
async function throttledUpdate(document2, updates, throttlingFunc = void 0, immediate = false) {
  if (!document2) return;
  updates = foundry.utils.expandObject(updates);
  updates = foundry.utils.mergeObject(updatesCache[document2.uuid] ?? {}, updates, { inplace: false, overwrite: true });
  updatesCache[document2.uuid] = updates;
  if (!debounceInterval || immediate || !throttlingFunc) {
    if (debugEnabled > 0) warn(`throttledU[date] | immediate update for ${document2.uuid}`, updates);
    return await _updateAction(document2, foundry.utils.duplicate(updates));
  }
  return await throttlingFunc(document2, foundry.utils.duplicate(updates));
}
__name(throttledUpdate, "throttledUpdate");
function getUpdatesCache(uuid) {
  if (!uuid) return {};
  return updatesCache[uuid] ?? {};
}
__name(getUpdatesCache, "getUpdatesCache");
function addUpdatesCacheFlags(uuid, updates, prepend = MODULE_ID) {
  if (!uuid) return;
  if (prepend) updates = { [prepend]: updates };
  updates = foundry.utils.expandObject({ flags: updates });
  updatesCache[uuid] = foundry.utils.mergeObject(updatesCache[uuid] ?? {}, updates, { insertKeys: true, insertValue: true });
}
__name(addUpdatesCacheFlags, "addUpdatesCacheFlags");
function addUpdatesCache(uuid, updates) {
  if (!uuid) return;
  updatesCache[uuid] = foundry.utils.mergeObject(updatesCache[uuid] ?? {}, updates, { insertKeys: true, insertValue: true });
}
__name(addUpdatesCache, "addUpdatesCache");
function clearUpdatesCache(uuid) {
  if (!uuid) return;
  delete updatesCache[uuid];
}
__name(clearUpdatesCache, "clearUpdatesCache");
function getCachedDocument2(uuid) {
  if (!uuid) return void 0;
  let document2 = fromUuidSync(uuid);
  let updates = document2?.uuid && updatesCache[document2.uuid];
  if (!foundry.utils.isEmpty(updates))
    document2 = document2.clone(updates, { changes: updates, keepId: true });
  return document2;
}
__name(getCachedDocument2, "getCachedDocument");
function isEffectExpired(effect) {
  if (installedModules.get("times-up") && globalThis.TimesUp.isEffectExpired) {
    return globalThis.TimesUp.isEffectExpired(effect);
  }
  return effect.duration.remaining <= 0;
}
__name(isEffectExpired, "isEffectExpired");
async function expireEffects(actor, effects, options) {
  if (!effects) return {};
  const actorEffectsToDelete = [];
  const effectsToDelete = [];
  const effectsToDisable = [];
  for (let effect of effects) {
    if (!effect.id) continue;
    if (!fromUuidSync(effect.uuid)) continue;
    if (effect.transfer)
      effectsToDisable.push(effect);
    else if (effect.parent instanceof Actor)
      actorEffectsToDelete.push(effect.id);
    else if (effect.parent instanceof Item)
      effectsToDelete.push(effect);
  }
  if (actorEffectsToDelete.length > 0) {
    await actor.deleteEmbeddedDocuments("ActiveEffect", actorEffectsToDelete, options);
  }
  if (effectsToDisable.length > 0) {
    for (let effect of effectsToDisable) {
      await effect.update({ "disabled": true }, options);
    }
  }
  if (effectsToDelete.length > 0) {
    for (let effect of effectsToDelete)
      await effect.delete(options);
  }
  return { deleted: actorEffectsToDelete, disabled: effectsToDisable, itemEffects: effectsToDelete };
}
__name(expireEffects, "expireEffects");
async function processConcentrationRequestMessage(message, html, data) {
  if (configSettings.doConcentrationCheck !== "chat") return;
  let elt = html.querySelectorAll("[data-action=concentration]");
  const hasRolled = foundry.utils.getProperty(message, `flags.${MODULE_ID}.concentrationRolled`);
  if (hasRolled || !preferredActiveGM()?.isSelf) return;
  if (elt.length === 1 && !hasRolled) {
    let { action, dc: dcStr, type } = elt[0].dataset;
    let token, actor;
    if (action === "concentration" && type === "midi-concentration") {
      let dc = Number(dcStr);
      let { actor: actorId, alias, scene, token: tokenId } = message.speaker;
      if (scene && tokenId) token = game.scenes?.get(scene)?.tokens.get(tokenId);
      if (token) actor = token.actor;
      if (!actor && actorId) actor = game.actors?.get(actorId);
      if (actor) {
        await message.setFlag(MODULE_ID, "concentrationRolled", true);
        const user = playerForActor(actor);
        if (user?.active) {
          socketlibSocket.executeAsUser("rollConcentration", user.id, { actorUuid: actor.uuid, target: dc, create: true, rollMode: "gmroll" });
        } else actor.rollConcentration({ legacy: false, target: dc }, {}, { create: true, rollMode: "gmroll" });
      }
    }
  }
}
__name(processConcentrationRequestMessage, "processConcentrationRequestMessage");
function setRollOperatorEvaluated(roll) {
  if (!roll._evaluated) return roll;
  roll.terms.forEach((t) => {
    if (!t._evaluated) t.evaluate();
  });
}
__name(setRollOperatorEvaluated, "setRollOperatorEvaluated");
function doSyncRoll(roll, source) {
  if (!roll.isDeterministic) {
    error2(`%c doSyncRoll | dice expressions not supported in v12 [${roll.formula}] and will be ignored ${source}`, "color:red;");
    return new Roll("0").evaluateSync();
  } else
    return roll.evaluateSync();
}
__name(doSyncRoll, "doSyncRoll");
async function addDependent(document2, dependent) {
  if (!document2?.uuid) {
    error2(`midi-addDependent | document ${document2?.name} does not have an uuid`);
    return;
  }
  if (!dependent?.uuid) {
    error2(`midi-addDependent | dependent ${dependent?.name} does not have an uuid`);
    return;
  }
  if (game.user?.isGM || dependent.isOwner) {
    return dependent.setFlag("dnd5e", "dependentOn", document2.uuid);
  } else {
    return unTimedExecuteAsGM("addDependent", { documentUuid: document2.uuid, dependentUuid: dependent.uuid });
  }
}
__name(addDependent, "addDependent");
async function addConcentrationDependent(actorRef, dependent, item) {
  if (dependent instanceof Token2) dependent = dependent.document;
  if (!dependent.uuid) {
    console.warn(`midi-qol | addConcentrationDependent | dependent ${dependent?.name} must have a uuid`);
    return void 0;
  }
  const actor = getActor(actorRef);
  if (!actor) {
    console.warn(`midi-qol | addConcentrationDependent | actor not found for ${actorRef}`);
    return void 0;
  }
  if (!item) {
    log("addConcentrationDependent | item not supplied - using any concentration effect");
  }
  const concentrationEffect = getConcentrationEffect(actor, item);
  if (!concentrationEffect) {
    console.warn(`midi-qol | addConcentrationDependent | dnd5e concentration effect not found for ${actor.name} ${item?.name ?? "no item"}`);
    return void 0;
  }
  if (game.user?.isGM || actor.isOwner) {
    return dependent.setFlag("dnd5e", "dependentOn", concentrationEffect.uuid);
  } else
    return unTimedExecuteAsGM("addDependent", { documentUuid: concentrationEffect.uuid, dependentUuid: dependent.uuid });
}
__name(addConcentrationDependent, "addConcentrationDependent");
function getAppliedEffects(actor, { includeEnchantments }) {
  if (!actor) return [];
  let effects = actor.appliedEffects;
  if (includeEnchantments) {
    const enchantments = actor.items.contents.flatMap((i) => i.effects.contents).filter((ae) => ae.isAppliedEnchantment);
    effects = effects.concat(enchantments);
  }
  return effects;
}
__name(getAppliedEffects, "getAppliedEffects");
function getCEEffectByName2(name) {
  return ceInterface?.findEffect({ effectName: name });
}
__name(getCEEffectByName2, "getCEEffectByName");
async function CEAddEffectWith2(options) {
  return ceInterface?.addEffect(options);
}
__name(CEAddEffectWith2, "CEAddEffectWith");
async function CERemoveEffect(options) {
  return ceInterface?.removeEffect(options);
}
__name(CERemoveEffect, "CERemoveEffect");
async function CEToggleEffect(options) {
  const { effectName, uuid, effectId, origin, overlay } = options;
  return ceInterface?.toggleEffect({ uuids: [uuid], effectName, effectId, origin, overlay });
}
__name(CEToggleEffect, "CEToggleEffect");
function CEHasEffectApplied(options) {
  return !!ceInterface?.hasEffectApplied(options);
}
__name(CEHasEffectApplied, "CEHasEffectApplied");
function getActivityDefaultDamageType(workflow) {
  let defaultDamageType = workflow.activity.damage?.parts[0]?.types.first();
  if (defaultDamageType) return defaultDamageType;
  if (workflow.defaultDamageType) defaultDamageType = workflow.defaultDamageType;
  if (!defaultDamageType) defaultDamageType = MidiQOL.MQdefaultDamageType;
  return defaultDamageType;
}
__name(getActivityDefaultDamageType, "getActivityDefaultDamageType");
function activityHasAreaTarget(activity) {
  return (activity?.target?.template.type ?? "") in GameSystemConfig.areaTargetTypes;
}
__name(activityHasAreaTarget, "activityHasAreaTarget");
function convertDuration(durationData, inCombat, maxRoundsForTurns = 10) {
  const timesUpActive = installedModules.get("times-up");
  const useTurns = inCombat && timesUpActive;
  const roundTime = CONFIG.time.roundTime ?? 6;
  if (!durationData?.units) {
    return useTurns ? { type: "turns", seconds: 0, rounds: 0, turns: 1 } : { type: "seconds", seconds: 1, rounds: 0, turns: 0 };
  }
  const { units, value } = durationData;
  if (["spec", "perm", "disp", "dstr"].includes(units)) {
    return { type: "none", seconds: void 0, rounds: void 0, turns: void 0 };
  }
  if (units === "inst") {
    return useTurns ? { type: "turns", seconds: 0, rounds: 0, turns: 1 } : { type: "seconds", seconds: 1, rounds: 0, turns: 0 };
  }
  if (["turn", "turns"].includes(units)) {
    return {
      type: useTurns ? "turns" : "seconds",
      seconds: roundTime,
      rounds: 0,
      turns: value
    };
  }
  if (["round", "rounds"].includes(units)) {
    return {
      type: useTurns ? "turns" : "seconds",
      seconds: value * roundTime,
      rounds: value,
      turns: 0
    };
  }
  const calendar = game.time?.calendar;
  const secondsPerMinute = calendar?.days?.secondsPerMinute ?? 60;
  const minutesPerHour = calendar?.days?.minutesPerHour ?? 60;
  const hoursPerDay = calendar?.days?.hoursPerDay ?? 24;
  const daysPerWeek = calendar?.days?.values?.length ?? 7;
  const secondsPerHour = secondsPerMinute * minutesPerHour;
  const secondsPerDay = secondsPerHour * hoursPerDay;
  let seconds;
  switch (units) {
    case "second":
    case "seconds":
      seconds = value;
      break;
    case "minute":
    case "minutes":
      seconds = value * secondsPerMinute;
      break;
    case "hour":
    case "hours":
      seconds = value * secondsPerHour;
      break;
    case "day":
    case "days":
      seconds = value * secondsPerDay;
      break;
    case "week":
    case "weeks":
      seconds = value * secondsPerDay * daysPerWeek;
      break;
    case "month":
    case "months":
      seconds = value * secondsPerDay * 30;
      break;
    case "year":
    case "years":
      seconds = value * secondsPerDay * 365;
      break;
    default:
      warn(`midi-qol | convertDuration | Unknown duration unit: ${units}`);
      return { type: "none", seconds: void 0, rounds: void 0, turns: void 0 };
  }
  const rounds = Math.floor(seconds / roundTime);
  if (useTurns && rounds <= maxRoundsForTurns) {
    return { type: "turns", seconds, rounds, turns: 0 };
  }
  return { type: "seconds", seconds, rounds, turns: 0 };
}
__name(convertDuration, "convertDuration");
function getSaveRollModeFor(abilityId) {
  if (configSettings.rollChecksBlind.includes("all") || configSettings.rollChecksBlind.includes(abilityId))
    return "blindroll";
  return configSettings.autoCheckSaves !== "allShow" ? "gmroll" : "public";
}
__name(getSaveRollModeFor, "getSaveRollModeFor");
function getCheckRollModeFor(abilityId) {
  if (configSettings.rollSavesBlind.includes("all") || configSettings.rollSavesBlind.includes(abilityId))
    return "blindroll";
  return configSettings.autoCheckSaves !== "allShow" ? "gmroll" : "public";
}
__name(getCheckRollModeFor, "getCheckRollModeFor");
function areMidiKeysPressed(event, action) {
  if (!event) return false;
  const activeModifiers = {};
  const KeyBoardManager = game.keyboard;
  const MODIFIER_KEYS = KeyBoardManager.constructor.MODIFIER_KEYS;
  const MODIFIER_CODES = KeyBoardManager.constructor.MODIFIER_CODES;
  const ClientKeyBindings = game.keybindings;
  const addModifiers = /* @__PURE__ */ __name((key, pressed) => {
    activeModifiers[key] = pressed;
    MODIFIER_CODES[key].forEach((n) => activeModifiers[n] = pressed);
  }, "addModifiers");
  addModifiers(MODIFIER_KEYS.CONTROL, event.ctrlKey || event.metaKey);
  addModifiers(MODIFIER_KEYS.SHIFT, event.shiftKey);
  addModifiers(MODIFIER_KEYS.ALT, event.altKey);
  return ClientKeyBindings?.get("midi-qol", action).some((b) => {
    if (KeyBoardManager?.downKeys.has(b.key) && b.modifiers.every((m) => activeModifiers[m])) return true;
    if (b.modifiers?.length) return false;
    return activeModifiers[b.key];
  });
}
__name(areMidiKeysPressed, "areMidiKeysPressed");
function setRangedTargets(tokenToUse, targetDetails) {
  if (!canvas || !canvas.scene) return true;
  if (!tokenToUse) {
    ui.notifications?.warn(`${i18n("midi-qol.noSelection")}`);
    return true;
  }
  let dispositions = targetDetails.affects.type === "creature" ? [-1, 0, 1] : targetDetails.affects.type === "ally" ? [tokenToUse.document.disposition] : [-tokenToUse.document.disposition];
  game.user?.targets.forEach((t) => {
    t.setTarget(false, { releaseOthers: false });
  });
  game.user?.targets.clear();
  let minDist = targetDetails.template.size;
  const targetIds = [];
  const maxTargets = targetDetails.affects?.count;
  let ignoreToken = (targetDetails.affects.special ?? "").split(";").some((spec) => ["-self"].includes(spec)) ? tokenToUse : null;
  if ((targetDetails.affects.special ?? "").split(";").some((spec) => "self" === spec)) {
    foundry.utils.logCompatibilityWarning(
      "midi-qol | target specials includes 'self'. Use '-self' to exclude the caster",
      { since: "midi-qol 13.0.19", until: "13.1.0" }
    );
  }
  if (canvas.tokens?.placeables && canvas.grid) {
    const canvasTokens = canvas.tokens?.placeables;
    if (canvasTokens) for (let target of canvasTokens) {
      const targetDocument = target.document;
      if (maxTargets !== "" && (targetIds.length ?? 0) >= Number(maxTargets)) break;
      if (!isValidTarget(target)) continue;
      const ray = new foundry.canvas.geometry.Ray(target.center, tokenToUse.center);
      const wallsBlock = ["wallsBlock", "wallsBlockIgnoreDefeated", "wallsBlockIgnoreIncapacitated"].includes(configSettings.rangeTarget);
      let inRange = target.actor && dispositions.includes(targetDocument.disposition);
      if (target.actor && ["wallsBlockIgnoreIncapacited", "alwaysIgnoreIncapacitated"].includes(configSettings.rangeTarget))
        inRange = inRange && !checkIncapacitated(target.actor, debugEnabled > 0, false);
      if (["wallsBlockIgnoreDefeated", "alwaysIgnoreDefeated"].includes(configSettings.rangeTarget))
        inRange = inRange && !checkDefeated(target);
      inRange = inRange && (configSettings.rangeTarget === "none" || !hasWallBlockingCondition(target));
      if (inRange) {
        if (ignoreToken?.document.uuid === target.document.uuid) {
          inRange = false;
        }
        const distance = getDistance(target, tokenToUse, { wallsBlock });
        inRange = inRange && distance >= 0 && distance <= Number(minDist);
      }
      if (inRange) {
        target.setTarget(true, { user: game.user, releaseOthers: false });
        if (target.document.id) targetIds.push(target.document.id);
      }
    }
    game.user?.broadcastActivity({ targets: targetIds });
  }
  return true;
}
__name(setRangedTargets, "setRangedTargets");
function getCPREffect(id) {
  if (!game.modules.get("chris-premades")?.active || !game.settings.get("chris-premades", "effectInterface")) return void 0;
  let cprItem = game.items?.find((i) => i.flags["chris-premades"]?.effectInterface);
  return cprItem?.effects.get(id);
}
__name(getCPREffect, "getCPREffect");
function updateUserTargets(targetIds) {
  canvas.tokens?.setTargets(targetIds);
}
__name(updateUserTargets, "updateUserTargets");
var _savedTargetIds = null;
var _targetSaveDepth = 0;
var _userInitiatedTarget = false;
function setUserInitiatedTarget(value) {
  _userInitiatedTarget = value;
}
__name(setUserInitiatedTarget, "setUserInitiatedTarget");
function pushTargetSave() {
  if (_targetSaveDepth === 0) {
    _savedTargetIds = Array.from(game.user?.targets ?? []).map((t) => t.id);
  }
  _targetSaveDepth++;
}
__name(pushTargetSave, "pushTargetSave");
function popTargetSave() {
  _targetSaveDepth--;
  const willRestore = _targetSaveDepth <= 0 && _savedTargetIds !== null;
  if (willRestore) {
    updateUserTargets(_savedTargetIds);
    _savedTargetIds = null;
    _targetSaveDepth = 0;
  }
}
__name(popTargetSave, "popTargetSave");
function clearTargetSave() {
  _savedTargetIds = null;
  _targetSaveDepth = 0;
}
__name(clearTargetSave, "clearTargetSave");
function onUserManualTarget(user) {
  if (user === game.user && _userInitiatedTarget) {
    clearTargetSave();
  }
}
__name(onUserManualTarget, "onUserManualTarget");
async function cleanCPRFlanked() {
  const flankedId = getStaticID("flanked");
  let cprEffect = getCPREffect(flankedId);
  if (!cprEffect) return;
  let updateNeeded = false;
  const changes = foundry.utils.deepClone(cprEffect.changes);
  changes.forEach((c) => {
    if (c.key === "flags.midi-qol.grants.attack.advantage.mwak") {
      c.key = "flags.midi-qol.grants.advantage.attack.mwak";
      updateNeeded = true;
    }
    if (c.key === "flags.midi-qol.grants.attack.advantage.msak") {
      c.key = "flags.midi-qol.grants.advantage.attack.msak";
      updateNeeded = true;
    }
  });
  if (updateNeeded) {
    await cprEffect.update({ "changes": changes });
    console.warn("midi-qol | cleanCPRFlanked | corrected flanked effect", cprEffect);
  }
  cprEffect = getCPREffect(flankedId);
}
__name(cleanCPRFlanked, "cleanCPRFlanked");
function getItemFromEffectOrigin(origin, alreadyVisited = []) {
  let originItem;
  if (alreadyVisited.includes(origin)) return;
  const originDoc = fromUuidSync(origin);
  if (originDoc instanceof Item) return originDoc;
  if (originDoc?.item instanceof Item) return originDoc.item;
  if (originDoc?.parent instanceof Item) {
    originItem = originDoc.parent;
  } else if (typeof originDoc?.origin === "string") {
    originItem = getItemFromEffectOrigin(originDoc.origin, [...alreadyVisited, origin]);
  }
  if (!originItem && (origin.includes(".ActiveEffect.") || origin.includes(".Activity."))) {
    const itemUuid = origin.replace(/\.(?:ActiveEffect|Activity)\.[^.]+$/, "");
    let item = fromUuidSync(itemUuid);
    if (!(item instanceof Item) && itemUuid.includes(".Actor.")) {
      const withoutActor = itemUuid.replace(/\.Actor\.[^.]+/, "");
      item = fromUuidSync(withoutActor);
    }
    if (item instanceof Item) originItem = item;
  }
  return originItem;
}
__name(getItemFromEffectOrigin, "getItemFromEffectOrigin");
var toCodepoints = /* @__PURE__ */ __name((s) => Array.from(s), "toCodepoints");
function normalizeWithMap(str, options) {
  const norm = [];
  const map = [];
  const chars = toCodepoints(str);
  for (let i = 0; i < chars.length; i++) {
    const ch = chars[i];
    if (options?.ignoreWhitespace && /\s/.test(ch) && ch !== "\n" && ch !== "\r") continue;
    if (options?.ignoreNewlines && (ch === "\n" || ch === "\r")) continue;
    norm.push(ch);
    map.push(i);
  }
  return { norm, map };
}
__name(normalizeWithMap, "normalizeWithMap");
function diffStringsFull(a, b, options) {
  if (a === b) return [{ type: "equal", text: a }];
  const { norm: A, map: mapA } = normalizeWithMap(a, options);
  const { norm: B, map: mapB } = normalizeWithMap(b, options);
  const n = A.length;
  const m = B.length;
  if (n === 0) return [{ type: "insert", text: b }];
  if (m === 0) return [{ type: "delete", text: a }];
  const max = n + m;
  const trace = [];
  let V = /* @__PURE__ */ new Map();
  V.set(1, 0);
  for (let d = 0; d <= max; d++) {
    const Vnext = /* @__PURE__ */ new Map();
    for (let k = -d; k <= d; k += 2) {
      let x;
      if (k === -d || k !== d && (V.get(k - 1) ?? -Infinity) < (V.get(k + 1) ?? -Infinity)) {
        x = V.get(k + 1) ?? 0;
      } else {
        x = (V.get(k - 1) ?? 0) + 1;
      }
      let y = x - k;
      while (x < n && y < m && A[x] === B[y]) {
        x++;
        y++;
      }
      Vnext.set(k, x);
      if (x >= n && y >= m) {
        trace.push(Vnext);
        const opsRev = [];
        let kk = k, xx = x, yy = y;
        for (let D = d; D > 0; D--) {
          const Vprev = trace[D - 1];
          let kPrev;
          if (kk === -D || kk !== D && (Vprev.get(kk - 1) ?? -Infinity) < (Vprev.get(kk + 1) ?? -Infinity)) {
            kPrev = kk + 1;
          } else {
            kPrev = kk - 1;
          }
          const xPrev = Vprev.get(kPrev);
          const yPrev = xPrev - kPrev;
          while (xx > xPrev && yy > yPrev) {
            xx--;
            yy--;
            opsRev.push({ type: "equal", text: substringFromMap(a, mapA, xx, xx + 1) });
          }
          if (xPrev < xx) {
            opsRev.push({ type: "delete", text: substringFromMap(a, mapA, xPrev, xPrev + 1) });
          } else {
            opsRev.push({ type: "insert", text: substringFromMap(b, mapB, yPrev, yPrev + 1) });
          }
          kk = kPrev;
          xx = xPrev;
          yy = yPrev;
        }
        while (xx > 0 && yy > 0) {
          xx--;
          yy--;
          opsRev.push({ type: "equal", text: substringFromMap(a, mapA, xx, xx + 1) });
        }
        opsRev.reverse();
        return coalesceOps(opsRev);
      }
    }
    trace.push(Vnext);
    V = Vnext;
  }
  return [{ type: "replace", text: b, from: a }];
}
__name(diffStringsFull, "diffStringsFull");
function substringFromMap(original, map, start, end) {
  const chars = toCodepoints(original);
  return chars.slice(map[start], map[end - 1] + 1).join("");
}
__name(substringFromMap, "substringFromMap");
function coalesceOps(ops) {
  if (ops.length === 0) return ops;
  const merged = [];
  for (const op of ops) {
    const last = merged[merged.length - 1];
    if (last && last.type === op.type && op.type !== "replace") {
      last.text += op.text;
    } else {
      merged.push({ ...op });
    }
  }
  const result = [];
  for (let i = 0; i < merged.length; i++) {
    const cur = merged[i];
    const next = merged[i + 1];
    if (cur && next && cur.type === "delete" && next.type === "insert") {
      result.push({ type: "replace", from: cur.text, text: next.text });
      i++;
    } else {
      result.push(cur);
    }
  }
  const final = [];
  for (const op of result) {
    const last = final[final.length - 1];
    if (last && last.type === "equal" && op.type === "equal") {
      last.text += op.text;
    } else {
      final.push(op);
    }
  }
  return final;
}
__name(coalesceOps, "coalesceOps");
function getTargetDescriptor(uuid, targetDescriptors) {
  if (!uuid || !targetDescriptors) return void 0;
  return targetDescriptors?.find((td) => td.uuid === uuid);
}
__name(getTargetDescriptor, "getTargetDescriptor");
var throttledScrollBottom = foundry.utils.throttle(() => {
  ui.chat?.scrollBottom({ waitImages: true });
}, 150);

// src/module/GMAction.ts
var socketlibSocket = void 0;
var traitList = { di: "", dr: "", dv: "", dm: "", da: "" };
var setupSocket = /* @__PURE__ */ __name(() => {
  socketlibSocket = globalThis.socketlib.registerModule(MODULE_ID);
  socketlibSocket.register("_gmSetFlag", _gmSetFlag);
  socketlibSocket.register("_gmUnsetFlag", _gmUnsetFlag);
  socketlibSocket.register("addConvenientEffect", addConvenientEffect);
  socketlibSocket.register("addDependent", _addDependent);
  socketlibSocket.register("applyEffects", _applyEffects);
  socketlibSocket.register("bonusCheck", _bonusCheck);
  socketlibSocket.register("chooseReactions", localDoReactions);
  socketlibSocket.register("completeItemUse", _completeItemUse);
  socketlibSocket.register("completeItemUseV2", _completeItemUse);
  socketlibSocket.register("completeActivityUse", _completeActivityUse);
  socketlibSocket.register("confirmDamageRollComplete", confirmDamageRollComplete);
  socketlibSocket.register("confirmDamageRollCompleteHit", confirmDamageRollCompleteHit);
  socketlibSocket.register("confirmDamageRollCompleteMiss", confirmDamageRollCompleteMiss);
  socketlibSocket.register("cancelWorkflow", cancelWorkflow);
  socketlibSocket.register("createActor", createActor);
  socketlibSocket.register("createChatMessage", _createChatMessage);
  socketlibSocket.register("createEffects", createEffects);
  socketlibSocket.register("createReverseDamageCard", createReverseDamageCard);
  socketlibSocket.register("D20Roll", _D20Roll);
  socketlibSocket.register("ddbglPendingFired", ddbglPendingFired);
  socketlibSocket.register("deleteEffects", deleteEffects);
  socketlibSocket.register("deleteEffectsByUuid", deleteEffectsByUuid);
  socketlibSocket.register("deleteItemEffects", deleteItemEffects);
  socketlibSocket.register("deleteToken", deleteToken);
  socketlibSocket.register("toggleTokenHidden", toggleTokenHidden);
  socketlibSocket.register("gmOverTimeEffect", _gmOverTimeEffect);
  socketlibSocket.register("localActivityOverTimeEffect", _localActivityOverTimeEffect);
  socketlibSocket.register("monksTokenBarSaves", monksTokenBarSaves);
  socketlibSocket.register("flashTokenBarSaves", flashTokenBarSaves);
  socketlibSocket.register("moveToken", _moveToken);
  socketlibSocket.register("moveTokenAwayFromPoint", _moveTokenAwayFromPoint);
  socketlibSocket.register("teleportToken", _teleportToken);
  socketlibSocket.register("queueUndoData", queueUndoData);
  socketlibSocket.register("queueUndoDataDirect", _queueUndoDataDirect);
  socketlibSocket.register("removeEffect", _removeEffect);
  socketlibSocket.register("removeCEEffect", _removeCEEffect);
  socketlibSocket.register("removeEffects", removeEffects);
  socketlibSocket.register("removeEffectUuids", removeEffectUuids);
  socketlibSocket.register("removeMostRecentWorkflow", _removeMostRecentWorkflow);
  socketlibSocket.register("removeStatsForActorId", removeActorStats);
  socketlibSocket.register("removeWorkflow", _removeWorkflow);
  socketlibSocket.register("rollAbility", rollAbility);
  socketlibSocket.register("rollAbilityV2", rollAbility);
  socketlibSocket.register("rollConcentration", rollConcentration);
  socketlibSocket.register("startUndoWorkflow", startUndoWorkflow);
  socketlibSocket.register("undoMostRecentWorkflow", _undoMostRecentWorkflow);
  socketlibSocket.register("undoTillWorkflow", undoTillWorkflow);
  socketlibSocket.register("updateActor", updateActor);
  socketlibSocket.register("updateEffects", updateEffects);
  socketlibSocket.register("updateItems", updateItems);
  socketlibSocket.register("recordStats", GMrecordStats);
  socketlibSocket.register("saveAllStats", GMsaveAllStats);
  socketlibSocket.register("updateUndoChatCardUuids", updateUndoChatCardUuids);
  socketlibSocket.register("updateUndoChatCardUuidsById", updateUndoChatCardUuidsById);
  socketlibSocket.register("removeActionBonusReaction", removeActionBonusReaction);
  socketlibSocket.register("rollActionSave", rollActionSave);
  socketlibSocket.register("toggleStatusEffect", _toggleStatusEffect);
}, "setupSocket");
async function _removeWorkflow(workflowId) {
  return Workflow.removeWorkflow(workflowId);
}
__name(_removeWorkflow, "_removeWorkflow");
var SaferSocket = class {
  static {
    __name(this, "SaferSocket");
  }
  #_socketlibSocket;
  constructor(socketlibSocket2) {
    this.#_socketlibSocket = socketlibSocket2;
  }
  canCall(handler) {
    if (game.user?.isGM) return true;
    switch (handler) {
      case "addDependent":
      case "applyEffects":
      case "bonusCheck":
      case "chooseReactions":
      case "completeItemUse":
      case "completeActivityUse":
      case "confirmDamageRollComplete":
      case "confirmDamageRollCompleteHit":
      case "confirmDamageRollCompleteMiss":
      case "cancelWorkflow":
      case "createChatMessage":
      case "createEffects":
      case "D20Roll":
      case "log":
      case "monksTokenBarSaves":
      case "flashTokenBarSaves":
      case "moveToken":
      case "moveTokenAwayFromPoint":
      case "teleportToken":
      case "removeWorkflow":
      case "rollAbility":
      case "rollAbilityV2":
      case "rollConcentration":
      case "removeEffect":
      case "removeActionBonusReaction":
      case "recordStats":
      case "toggleStatusEffect":
      case "toggleTokenHidden":
        return true;
      case "addConvenientEffect":
      case "createActor":
      case "createReverseDamageCard":
      case "deleteEffects":
      case "deleteItemEffects":
      case "deleteToken":
      case "removeEffects":
      case "removeEffectUuids":
      case "updateActor":
      case "updateEffects":
      case "updateItems":
      case "_gmSetFlag":
      case "_gmUnsetFlag":
        if (game.user?.isTrusted) return true;
        ui.notifications?.warn(`midi-qol | user ${game.user?.name} must be a trusted player to call ${handler} and will be disabled in the future`);
        return true;
      // TODO change this to false in the future.
      case "ddbglPendingFired":
      case "gmOverTimeEffect":
      case "queueUndoData":
      case "queueUndoDataDirect":
      case "removeStatsForActorId":
      case "removeMostRecentWorkflow":
      case "startUndoWorkflow":
      case "undoMostRecentWorkflow":
      case "undoTillWorkflow":
      case "saveAllStats":
      case "updateUndoChatCardUuids":
      case "updateUndoChatCardUuidsById":
      case "deleteEffectsByUuid":
      default:
        error2(`Non-GMs are not allowed to call ${handler}`);
        return false;
    }
  }
  async executeAsGM(handler, ...args) {
    if (!this.canCall(handler)) return false;
    return await unTimedExecuteAsGM(handler, ...args);
  }
  async executeAsUser(handler, userId, ...args) {
    if (!this.canCall(handler)) return false;
    return await this.#_socketlibSocket.executeAsUser(handler, userId, ...args);
  }
  async executeForAllGMs(handler, ...args) {
    if (!this.canCall(handler)) return false;
    return await this.#_socketlibSocket.executeForAllGMs(handler, ...args);
  }
  async executeForOtherGMS(handler, ...args) {
    if (!this.canCall(handler)) return false;
    return await this.#_socketlibSocket.executeForOtherGMS(handler, ...args);
  }
  async executeForEveryone(handler, ...args) {
    if (!this.canCall(handler)) return false;
    return await this.#_socketlibSocket.executeForEveryone(handler, ...args);
  }
  async executeForOthers(handler, ...args) {
    if (!this.canCall(handler)) return false;
    return await this.#_socketlibSocket.executeForOthers(handler, ...args);
  }
  async executeForUsers(handler, recipients, ...args) {
    if (!this.canCall(handler)) return false;
    return await this.#_socketlibSocket.executeForUsers(handler, recipients, ...args);
  }
};
async function removeActionBonusReaction(data) {
  const actor = fromActorUuid(data.actorUuid);
  if (!actor) return;
  if (hasUsedAnyReaction(actor)) await removeReactionUsed(actor);
  if (hasUsedBonusAction(actor)) await removeBonusActionUsed(actor);
  if (hasUsedAction(actor)) return await removeActionUsed(actor);
  return;
}
__name(removeActionBonusReaction, "removeActionBonusReaction");
async function _removeEffect(data) {
  const effect = fromUuidSync(data.effectUuid);
  if (!effect) return;
  return effect.delete({});
}
__name(_removeEffect, "_removeEffect");
async function _removeCEEffect(data) {
  return CERemoveEffect({ effectName: data.effectName, uuid: data.uuid });
}
__name(_removeCEEffect, "_removeCEEffect");
async function cancelWorkflow(data) {
  const workflow = Workflow.getWorkflow(data.workflowId);
  if (workflow?.itemCardUuid !== data.itemCardUuid) {
    const itemCard = await fromUuid(data.itemCardUuid);
    if (itemCard) itemCard.delete();
    return void 0;
  }
  if (workflow) return workflow.performState(workflow.WorkflowState_Cancel);
  return void 0;
}
__name(cancelWorkflow, "cancelWorkflow");
async function confirmDamageRollComplete(data) {
  const workflow = Workflow.getWorkflow(data.workflowId);
  const activity = workflow?.activity;
  if (!workflow || workflow.itemCardUuid !== data.itemCardUuid) {
    Workflow.removeItemCardButtons(data.itemCardUuid, { removeAttackButtons: true, removeDamageButtons: true, removeConfirmButtons: true });
    return void 0;
  }
  const hasHits = workflow.hitTargets.size > 0 || workflow.hitTargetsEC.size > 0;
  if (workflow.currentAction === workflow.WorkflowState_AttackRollComplete || hasHits && activity?.hasDamage && (!workflow.damageRoll || workflow.currentAction !== workflow.WorkflowState_ConfirmRoll)) {
    return "midi-qol | You must roll damage before completing the roll - you can only confirm miss until then";
  }
  if (workflow.hitTargets.size === 0 && workflow.hitTargetsEC.size === 0) {
    return confirmDamageRollCompleteMiss(data);
  }
  return workflow.performState(workflow.WorkflowState_RollConfirmed);
}
__name(confirmDamageRollComplete, "confirmDamageRollComplete");
async function confirmDamageRollCompleteHit(data) {
  const workflow = Workflow.getWorkflow(data.workflowId);
  const activity = workflow?.activity;
  if (!workflow || workflow.itemCardUuid !== data.itemCardUuid) {
    Workflow.removeItemCardButtons(data.itemCardUuid, { removeAttackButtons: true, removeDamageButtons: true, removeConfirmButtons: true });
    return void 0;
  }
  if (activity?.hasDamage && !workflow.damageRoll || workflow.currentAction !== workflow.WorkflowState_ConfirmRoll) {
    return "midi-qol | You must roll damage before completing the roll - you can only confirm miss until then";
  }
  if (workflow.hitTargets.size === workflow.targets.size) {
    return workflow.performState(workflow.WorkflowState_RollConfirmed);
  }
  workflow.hitTargets = new Set(workflow.targets);
  workflow.hitTargetsEC = /* @__PURE__ */ new Set();
  const rollMode = safeGetGameSetting("core", "rollMode");
  workflow.isFumble = false;
  for (let hitDataKey in workflow.hitDisplayData) {
    workflow.hitDisplayData[hitDataKey].hitString = i18n("midi-qol.hits");
    workflow.hitDisplayData[hitDataKey].hitResultNumeric = "--";
    if (configSettings.highlightSuccess) {
      workflow.hitDisplayData[hitDataKey].hitStyle = "color: green;";
    }
  }
  await workflow.displayHits(workflow.whisperAttackCard, true);
  return await workflow.performState(workflow.WorkflowState_RollConfirmed);
}
__name(confirmDamageRollCompleteHit, "confirmDamageRollCompleteHit");
async function confirmDamageRollCompleteMiss(data) {
  const workflow = Workflow.getWorkflow(data.workflowId);
  const activity = workflow?.activity;
  if (!workflow || workflow.itemCardUuid !== data.itemCardUuid) {
    Workflow.removeItemCardButtons(data.itemCardUuid, { removeDamageButtons: true, removeAttackButtons: true, removeConfirmButtons: true });
    return void 0;
  }
  if (workflow.hitTargets.size > 0 || workflow.hitTargetsEC.size > 0) {
    workflow.hitTargets = /* @__PURE__ */ new Set();
    workflow.hitTargetsEC = /* @__PURE__ */ new Set();
    const rollMode = safeGetGameSetting("core", "rollMode");
    for (let hitDataKey in workflow.hitDisplayData) {
      workflow.hitDisplayData[hitDataKey].hitString = i18n("midi-qol.misses");
      if (configSettings.highlightSuccess) {
        workflow.hitDisplayData[hitDataKey].hitStyle = "color: red;";
      }
      workflow.hitDisplayData[hitDataKey].hitResultNumeric = "--";
    }
    await workflow.displayHits(workflow.whisperAttackCard, true);
  }
  return workflow.performState(workflow.WorkflowState_RollConfirmed).then(() => Workflow.removeWorkflow(workflow?.id));
}
__name(confirmDamageRollCompleteMiss, "confirmDamageRollCompleteMiss");
function paranoidCheck(action, actor, data) {
  return true;
}
__name(paranoidCheck, "paranoidCheck");
async function removeEffects(data) {
  debug("removeEffects started");
  let removeFunc = /* @__PURE__ */ __name(async () => {
    try {
      debug("removeFunc: remove effects started");
      const actor = fromActorUuid(data.actorUuid);
      if (configSettings.paranoidGM && !paranoidCheck("removeEffects", actor, data)) return "gmBlocked";
      const effectsToDelete = actor.appliedEffects.filter((ef) => data.effects.includes(ef.id));
      return await expireEffects(actor, effectsToDelete, data.options);
    } catch (err) {
      const message = `GMACTION: remove effects error for ${data?.actorUuid}`;
      console.warn(message, err);
      TroubleShooter.recordError(err, message);
    } finally {
      warn("removeFunc: remove effects completed");
    }
  }, "removeFunc");
  return removeFunc();
}
__name(removeEffects, "removeEffects");
async function removeEffectUuids(data) {
  debug("removeEffects started");
  let removeFunc = /* @__PURE__ */ __name(async () => {
    try {
      debug("removeFunc: remove effects started");
      const actor = fromActorUuid(data.actorUuid);
      if (configSettings.paranoidGM && !paranoidCheck("removeEffects", actor, data)) return "gmBlocked";
      const effectsToDelete = getAppliedEffects(actor, { includeEnchantments: true }).filter((ef) => data.effects.includes(ef.uuid));
      return await expireEffects(actor, effectsToDelete, data.options);
    } catch (err) {
      const message = `GMACTION: remove effects error for ${data?.actorUuid}`;
      console.warn(message, err);
      TroubleShooter.recordError(err, message);
    } finally {
      warn("removeFunc: remove effects completed");
    }
  }, "removeFunc");
  return removeFunc();
}
__name(removeEffectUuids, "removeEffectUuids");
async function createEffects(data) {
  const actor = fromActorUuid(data.actorUuid);
  for (let effect of data.effects) {
    if (effect.transfer === void 0) effect.transfer = false;
  }
  return await actor?.createEmbeddedDocuments("ActiveEffect", data.effects, data.options);
}
__name(createEffects, "createEffects");
async function updateEffects(data) {
  let parent = fromUuidSync(data.actorUuid);
  if (parent instanceof TokenDocument) parent = parent.actor;
  return parent?.updateEmbeddedDocuments("ActiveEffect", data.updates);
}
__name(updateEffects, "updateEffects");
async function updateItems(data) {
  const actor = fromActorUuid(data.actorUuid);
  return actor?.updateEmbeddedDocuments("Item", data.updates);
}
__name(updateItems, "updateItems");
async function _toggleStatusEffect(data) {
  const actor = fromActorUuid(data.actorUuid);
  return actor?.toggleStatusEffect(data.statusId, data.options);
}
__name(_toggleStatusEffect, "_toggleStatusEffect");
function removeActorStats(data) {
  return gameStats.GMremoveActorStats(data.actorId);
}
__name(removeActorStats, "removeActorStats");
function GMrecordStats(data) {
  return gameStats.GMrecordStats(data);
}
__name(GMrecordStats, "GMrecordStats");
function GMsaveAllStats() {
  return gameStats.GMsaveAllStats();
}
__name(GMsaveAllStats, "GMsaveAllStats");
async function timedExecuteAsGM(toDo, data) {
  if (false) return unTimedExecuteAsGM(toDo, data);
  const start = Date.now();
  data.playerId = game.user?.id;
  const returnValue = await unTimedExecuteAsGM(toDo, data);
  log(`executeAsGM: ${toDo} elapsed: ${Date.now() - start}ms`);
  return returnValue;
}
__name(timedExecuteAsGM, "timedExecuteAsGM");
function preferredActiveGM() {
  let preferredGMId = configSettings.preferredGM ?? "";
  if (preferredGMId !== "") {
    const preferredGM = game.users?.get(preferredGMId);
    if (preferredGM?.active) return preferredGM;
    log(`preferredGM ${preferredGMId} is not active`);
  }
  return game.users?.activeGM ?? null;
}
__name(preferredActiveGM, "preferredActiveGM");
async function unTimedExecuteAsGM(toDo, ...args) {
  if (!socketlibSocket) return void 0;
  const myScene = game.user?.viewedScene;
  const preferredGMId = configSettings.preferredGM ?? "";
  let preferredGM = game.users?.get(preferredGMId);
  if (!preferredGM?.active) {
    if (preferredGMId !== "") log(`preferredGM ${preferredGMId} is not active`);
    preferredGM = void 0;
  }
  let gmOnScene;
  if (preferredGM?.viewedScene === myScene) gmOnScene = [preferredGM];
  else if (game.user?.isGM && game.user?.viewedScene === myScene) gmOnScene = [game.user];
  else gmOnScene = game.users?.filter((u) => u.active && u.isGM && u.viewedScene === myScene);
  if (gmOnScene && gmOnScene.length > 0) return socketlibSocket.executeAsUser(toDo, gmOnScene[0].id, ...args);
  if (preferredGM) return socketlibSocket.executeAsUser(toDo, preferredGM.id, ...args);
  return socketlibSocket.executeAsGM(toDo, ...args);
}
__name(unTimedExecuteAsGM, "unTimedExecuteAsGM");
async function timedAwaitExecuteAsGM(toDo, data) {
  if (false) return await unTimedExecuteAsGM(toDo, data);
  const start = Date.now();
  const returnValue = await unTimedExecuteAsGM(toDo, data);
  log(`await executeAsGM: ${toDo} elapsed: ${Date.now() - start}ms`);
  return returnValue;
}
__name(timedAwaitExecuteAsGM, "timedAwaitExecuteAsGM");
async function _gmUnsetFlag(data) {
  let actor = fromUuidSync(data.actorUuid);
  actor = actor instanceof Item ? actor.actor : actor;
  if (!actor) return void 0;
  return actor.unsetFlag(data.base, data.key);
}
__name(_gmUnsetFlag, "_gmUnsetFlag");
async function _gmSetFlag(data) {
  let actor = fromUuidSync(data.actorUuid);
  actor = actor instanceof Item ? actor.actor : actor;
  if (!actor) return void 0;
  return actor.setFlag(data.base, data.key, data.value);
}
__name(_gmSetFlag, "_gmSetFlag");
async function _gmOverTimeEffect(data) {
  const actor = fromActorUuid(data.actorUuid);
  const effect = fromUuidSync(data.effectUuid);
  log("Called _gmOvertime", actor?.name, effect?.name);
  if (actor && effect) return gmOverTimeEffect(actor, effect, data.startTurn, data.options);
}
__name(_gmOverTimeEffect, "_gmOverTimeEffect");
async function _localActivityOverTimeEffect(data) {
  const actor = await fromUuid(data.actorUuid);
  const effect = await fromUuid(data.effectUuid);
  if (!actor || !effect) {
    const message = `localActivityOverTimeEffect | actor or effect not found actor: ${data.actorUuid} effect: ${data.effectUuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("GMAction failed"), message);
    return void 0;
  }
  ;
  log("Called _localActivityOverTimeEffect", actor?.name, effect?.name);
  return localActivityOverTimeEffect(actor, effect, data.startTurn, data.options);
}
__name(_localActivityOverTimeEffect, "_localActivityOverTimeEffect");
async function _bonusCheck(data) {
  const tokenOrActor = await fromUuid(data.actorUuid);
  const actor = tokenOrActor instanceof Actor ? tokenOrActor : tokenOrActor?.actor;
  const roll = Roll.fromJSON(data.result);
  if (actor) return await bonusCheck(actor, roll, data.rollType, data.selector);
  else return null;
}
__name(_bonusCheck, "_bonusCheck");
async function _applyEffects(data) {
  let result;
  try {
    const workflow = Workflow.getWorkflow(data.workflowId);
    if (!workflow) throw new Error(`_applyEffects | workflowId: ${data.workflowId} - workflow not found`);
    workflow.forceApplyEffects = true;
    const targets = /* @__PURE__ */ new Set();
    for (let targetUuid of data.targets) {
      const maybeToken = (await fromUuid(targetUuid))?.object;
      if (maybeToken) targets.add(maybeToken);
    }
    workflow.applicationTargets = targets;
    if (workflow.applicationTargets.size > 0) result = await workflow.performState(workflow.WorkflowState_ApplyDynamicEffects);
    return result;
  } catch (err) {
    const message = `_applyEffects | remote apply effects error`;
    console.warn(message, err);
    TroubleShooter.recordError(err, message);
  }
  return result;
}
__name(_applyEffects, "_applyEffects");
async function _completeActivityUse(data) {
  if (!game.user) return null;
  let { activityUuid, actorUuid, usage, dialog, message } = data;
  let actor = await fromUuid(actorUuid);
  if (actor?.actor) actor = actor.actor;
  if (!actor) return null;
  if (usage.midiOptions?.targetsToUse) {
    const targets = usage.midiOptions.targetsToUse.map((t) => fromUuidSync(t)?.object).filter((t) => isValidTarget(t));
    usage.midiOptions.targetsToUse = new Set(targets);
  }
  const workflow = await completeActivityUse2(activityUuid, usage, dialog, message);
  if (workflow && data.usage.midiOptions?.workflowData) return workflow.getSafeMacroData();
  else return true;
}
__name(_completeActivityUse, "_completeActivityUse");
async function _completeItemUse(data) {
  if (!game.user) return null;
  let { itemData, actorUuid, config, dialog, message } = data;
  let actor = await fromUuid(actorUuid);
  if (actor?.actor) actor = actor.actor;
  if (!actor) return null;
  let ownedItem = new CONFIG.Item.documentClass(itemData, { parent: actor });
  ownedItem.prepareData();
  ownedItem.prepareFinalAttributes();
  ownedItem.applyActiveEffects();
  const workflow = await completeItemUse(ownedItem, config, dialog, message);
  if (workflow && data.config?.midiOptions?.workflowData) return workflow.getMacroData();
  else return true;
}
__name(_completeItemUse, "_completeItemUse");
async function updateActor(data) {
  let actor = fromUuidSync(data.actorUuid);
  if (!actor) return;
  if (data.actorData) {
    console.warn(`midi-qol | updateActor actorData deprecated. Call await MidiQOL.socket().executeAsGM("updateActor"({ updates }) instead`);
    await actor.update(data.actorData);
  }
  if (data.updates) await actor.update(data.updates);
}
__name(updateActor, "updateActor");
async function createActor(data) {
  let actorsData = data.actorData instanceof Array ? data.actorData : [data.actorData];
  const actors = await CONFIG.Actor.documentClass.createDocuments(actorsData, data.context ?? {});
  return actors?.length ? actors.map((a) => a.id) : false;
}
__name(createActor, "createActor");
async function deleteToken(data) {
  const token = await fromUuid(data.tokenUuid);
  if (token) {
    token.delete();
  }
}
__name(deleteToken, "deleteToken");
async function toggleTokenHidden(data) {
  const token = await fromUuid(data.tokenUuid);
  if (token) {
    const hidden = data.force ?? !token.hidden;
    await token.update({ hidden });
  }
}
__name(toggleTokenHidden, "toggleTokenHidden");
async function deleteEffectsByUuid(data) {
  for (let effectUuid of data.effectsToDelete) {
    const effect = fromUuidSync(effectUuid);
    if (effect !== void 0 && !isEffectExpired(effect)) {
      if (effect.transfer)
        await effect.update({ disabled: true });
      else
        await effect.delete();
    }
  }
}
__name(deleteEffectsByUuid, "deleteEffectsByUuid");
async function deleteEffects(data) {
  const actor = fromActorUuid(data.actorUuid);
  if (!actor) return;
  let finalEffectsToDelete = actor.appliedEffects.filter((ef) => data.effectsToDelete.includes(ef.id) && !isEffectExpired(ef));
  try {
    if (debugEnabled > 0) warn("_deleteEffects started", actor.name, data.effectsToDelete, finalEffectsToDelete, data.options);
    const result = await expireEffects(actor, finalEffectsToDelete, data.options);
    if (debugEnabled > 0) warn("_deleteEffects completed", actor.name, data.effectsToDelete, finalEffectsToDelete, data.options);
    return result;
  } catch (err) {
    const message = `deleteEffects | remote delete effects error`;
    console.warn(message, err);
    TroubleShooter.recordError(err, message);
    return [];
  }
}
__name(deleteEffects, "deleteEffects");
async function deleteItemEffects(data) {
  debug("deleteItemEffects: started", globalThis.DAE?.actionQueue);
  let deleteFunc = /* @__PURE__ */ __name(async () => {
    let effectsToDelete;
    try {
      let { targets, origin, ignore, options } = data;
      for (let idData of targets) {
        let actor = idData.tokenUuid ? fromActorUuid(idData.tokenUuid) : idData.actorUuid ? fromActorUuid(idData.actorUuid) : void 0;
        if (!actor) {
          error2("GMAction:deleteItemEffects | could not find actor for ", idData.tokenUuid);
          continue;
        }
        let originEntity = await fromUuid(origin);
        if (!originEntity) {
          error2("GMAction:deleteItemEffects | could not find origin for ", origin);
          continue;
        }
        effectsToDelete = actor?.appliedEffects?.filter((ef) => {
          if (originEntity instanceof ActiveEffect) originEntity = originEntity.parent;
          return ef.origin === originEntity?.uuid && !ignore.includes(ef.uuid) && (!data.ignoreTransfer || !ef.transfer);
        });
        if (installedModules.get("times-up")) {
          if (globalThis.TimesUp.isEffectExpired) {
            effectsToDelete = effectsToDelete.filter((ef) => !globalThis.TimesUp.isEffectExpired(ef), {});
          } else effectsToDelete = effectsToDelete.filter((ef) => !(ef.updateDuration().remaining <= 0));
        }
        debug("deleteItemEffects: effectsToDelete ", actor.name, effectsToDelete, options);
        if (effectsToDelete?.length > 0) {
          try {
            options = foundry.utils.mergeObject(options ?? {}, { parent: actor, concentrationDeleted: true });
            if (debugEnabled > 0) warn("deleteItemEffects ", actor.name, effectsToDelete, options);
            await expireEffects(actor, effectsToDelete, options);
          } catch (err) {
            const message = `delete item effects failed for ${actor?.name} ${actor?.uuid}`;
            console.warn(message, err);
            TroubleShooter.recordError(err, message);
          }
          ;
        }
        if (debugEnabled > 0) warn("deleteItemEffects: completed", actor.name);
      }
      if (globalThis.Sequencer) await globalThis.Sequencer.EffectManager.endEffects({ origin });
    } catch (err) {
      const message = `delete item effects failed for ${data?.origin} ${effectsToDelete}`;
      console.warn(message, err);
      TroubleShooter.recordError(err, message);
    }
  }, "deleteFunc");
  return await deleteFunc();
}
__name(deleteItemEffects, "deleteItemEffects");
async function addConvenientEffect(options) {
  let { effectName, actorUuid, origin } = options;
  const actor = getActor(actorUuid);
  console.warn('midi-qol | Deprecated. Call await game.modules.get("dfreds-convenient-effects")?.api?.addEffect({ effectName, uuid: actorUuid, origin }) instead');
  if (!actor) return;
  return CEAddEffectWith2({ effectName, uuid: actor.uuid, origin, overlay: false });
}
__name(addConvenientEffect, "addConvenientEffect");
async function _addDependent(data) {
  if (foundry.utils.isNewerVersion(game.system.version, "5.1.99")) {
    const dependent = fromUuidSync(data.dependentUuid);
    if (!dependent) return;
    return dependent.setFlag("dnd5e", "dependentOn", data.documentUuid);
  } else {
    const document2 = fromUuidSync(data.documentUuid);
    if (document2?.addDependent) return document2.addDependent(data.dependentUuid);
  }
}
__name(_addDependent, "_addDependent");
async function localDoReactions(data) {
  if (data.options.itemUuid) {
    data.options.item = fromUuidSync(data.options.itemUuid);
  }
  if (data.options.activity && data.options.item) {
    data.options.activity = data.options.item.system.activities.get(data.options.activity._id);
  }
  const result = await promptReactions(data.tokenUuid, data.reactionActivityList, data.triggerTokenUuid, data.reactionFlavor, data.triggerType, data.options);
  return result;
}
__name(localDoReactions, "localDoReactions");
function initGMActionSetup() {
  traitList.di = i18n("DND5E.DamImm");
  traitList.dr = i18n("DND5E.DamRes");
  traitList.dv = i18n("DND5E.DamVuln");
  traitList.da = "da";
  traitList.dm = "dm";
  traitList.di = "di";
  traitList.dr = "dr";
  traitList.dv = "dv";
}
__name(initGMActionSetup, "initGMActionSetup");
async function _createChatMessage(data) {
  return await ChatMessage.create(data.chatData);
}
__name(_createChatMessage, "_createChatMessage");
async function _D20Roll(params) {
  const actor = fromActorUuid(params.targetUuid);
  if (!actor || !params.rollOptions) {
    error2(`GMAction.D20Roll | no actor for ${params.targetUuid}`);
    return {};
  }
  return new Promise(async (resolve) => {
    let timeoutId;
    let result;
    const D20Roll = CONFIG.Dice.D20Roll;
    if (configSettings.playerSaveTimeout > 0) timeoutId = setTimeout(async () => {
      warn(`Roll request for {actor.name}timed out. Doing roll`);
      params.rollOptions.fastForward = true;
      let result2 = await new D20Roll(params.formula, {}, params.rollOptions).roll();
      foundry.utils.setProperty(result2, "flags.midi-qol.rollType", params.midiType);
      resolve(result2 ?? {});
    }, configSettings.playerSaveTimeout * 1e3);
    const roll = new D20Roll();
    const { parts, data } = D20Roll.constructParts({ "mod": params.bonus, "cover": params.coverBonus });
    roll.data = data;
    roll.parts = parts;
    roll.options = { ...params.rollOptions };
    const config = {
      advantage: params.rollOptions?.advantageMode === D20Roll.ADV_MODE.ADVANTAGE,
      disadvantage: params.rollOptions?.advantageMode === D20Roll.ADV_MODE.DISADVANTAGE,
      rolls: [roll],
      dialog: true
    };
    const dialog = {
      options: {
        window: {
          title: params.flavor,
          subtitle: actor.name
        }
      }
    };
    const message = {
      create: true,
      data: { flavor: params.flavor },
      speaker: ChatMessage.getSpeaker({ actor }),
      rollMode: params.rollMode
    };
    const rolls = await D20Roll.build(config, dialog, message);
    if (timeoutId) clearTimeout(timeoutId);
    if (rolls) foundry.utils.setProperty(rolls[0], "flags.midi-qol.rollType", params.midiType);
    resolve(rolls[0] ?? {});
  });
}
__name(_D20Roll, "_D20Roll");
async function rollConcentration(data) {
  const actor = fromUuidSync(data.actorUuid);
  if (!actor) {
    error2(`GMAction.rollConcentration | no actor for ${data.actorUuid}`);
    return {};
  }
  return actor.rollConcentration({ target: data.target, legacy: false }, {}, { create: data.create, whisper: data.whisper, rollMode: data.rollMode });
}
__name(rollConcentration, "rollConcentration");
async function rollAbility(data) {
  if (!data.saveDetails && data.targetUuid) {
    foundry.utils.logCompatibilityWarning(
      "midi-qol | rollAbility called with legacy flat argument format. Use { saveDetails, displayOptions } instead.",
      { since: "midi-qol 13.0.44", until: "midi-qol 13.1.0" }
    );
    data.saveDetails = {
      actorUuid: data.targetUuid,
      rollType: data.request ?? "save",
      rollAbilities: data.ability ? [data.ability] : [],
      rollSkills: data.skill ? [data.skill] : [],
      rollTools: data.tool ? [data.tool] : [],
      advantage: data.advantage,
      disadvantage: data.disadvantage,
      isMagicSave: data.isMagicSave ?? false,
      isConcentrationCheck: data.isConcentrationCheck,
      isFriendly: false,
      rollDC: data.rollDC ?? 0,
      saveItemUuid: data.saveItemUuid ?? "",
      workflowOptions: data.workflowOptions ?? {},
      workflowId: data.workflowId ?? "",
      itemCardUuid: "",
      itemId: data.options?.itemId ?? "",
      modifierTracker: void 0
    };
    data.displayOptions = {
      fastForward: data.options?.fastForward,
      chatMessage: data.options?.chatMessage ?? data.options?.create,
      rollMode: data.options?.rollMode ?? data.rollMode
    };
  }
  const sd = data.saveDetails;
  const disp = data.displayOptions;
  const rollAbilities = sd.rollAbilities ?? [];
  const rollSkills = sd.rollSkills ?? [];
  const rollTools = sd.rollTools ?? [];
  const ability = rollAbilities[0];
  const skill = rollSkills[0];
  const tool = rollTools[0];
  const advantage = sd.advantage;
  const disadvantage = sd.disadvantage;
  const isMagicSave = sd.isMagicSave;
  const isConcentrationCheck = sd.isConcentrationCheck;
  const rollDC = disp?.showTargetDC !== false ? sd.rollDC : void 0;
  const saveItemUuid = sd.saveItemUuid;
  const workflowOptions = sd.workflowOptions;
  const workflowId = sd.workflowId;
  const itemId = sd.itemId;
  const itemCardUuid = sd.itemCardUuid;
  const advantageByChoice = sd.advantageByChoice;
  const fastForward = disp?.fastForward ?? false;
  const chatMessage = disp?.chatMessage ?? true;
  const rollMode = disp?.rollMode;
  let normalizedRequest = sd.rollType;
  if (normalizedRequest === "abil") normalizedRequest = "check";
  if (normalizedRequest === "test") normalizedRequest = "check";
  const actor = fromActorUuid(sd.actorUuid);
  if (!actor) {
    error2(`GMAction.rollAbility | no actor for ${sd.actorUuid}`);
    return {};
  }
  const midiOptions = {
    workflowId,
    rollAbilities,
    rollSkills,
    rollTools,
    advantageByChoice,
    recomputeAdvantage: true,
    target: rollDC,
    isMagicSave,
    saveItemUuid,
    isConcentrationCheck,
    workflowOptions,
    itemCardUuid,
    advantage,
    disadvantage
  };
  let config = {
    midiOptions,
    advantage,
    disadvantage,
    workflowOptions
  };
  if (advantageByChoice && typeof advantageByChoice === "object") {
    for (const [key, value] of Object.entries(advantageByChoice)) {
      if (value?.tracker && !(value.tracker instanceof RollModifierTracker)) {
        const trackerData = value.tracker;
        value.tracker = new RollModifierTracker();
        value.tracker.restore(trackerData);
      }
    }
  }
  if (ability) config.ability = ability;
  if (skill) config.skill = skill;
  if (tool) config.tool = tool;
  let dialog = { configure: !fastForward };
  let message = { create: chatMessage, rollMode };
  if (normalizedRequest === "tool") {
    const toolsToTry = rollTools.length > 0 ? rollTools : tool ? [tool] : [];
    let toolItem;
    let foundToolId;
    for (const toolId of toolsToTry) {
      if (!toolId) continue;
      toolItem = actor.items.get(itemId ?? "");
      if (!toolItem || toolItem.type !== "tool") toolItem = actor.items.find((i) => i.type === "tool" && i.system.type.baseItem === toolId);
      if (toolItem) {
        foundToolId = toolId;
        break;
      }
    }
    if (!toolItem) {
      return [await new CONFIG.Dice.D20Roll("-1").roll()];
    } else {
      config.bonus = toolItem?.system.bonus ?? 0;
      config.prof = toolItem?.system.prof;
      config.item = toolItem;
      if (foundToolId && foundToolId !== tool) config.tool = foundToolId;
    }
  }
  if (rollMode) message.rollMode = rollMode;
  return new Promise(async (resolve) => {
    let timeoutId;
    let result;
    if (configSettings.playerSaveTimeout > 0) timeoutId = setTimeout(async () => {
      warn(`Roll request for ${actor.name} timed out. Doing roll`);
      dialog.configure = false;
      if (normalizedRequest === "save") result = await actor.rollSavingThrow(config, dialog, message);
      else if (normalizedRequest === "check") result = await actor.rollAbilityCheck(config, dialog, message);
      else if (normalizedRequest === "skill") result = await actor.rollSkill(config, dialog, message);
      else if (normalizedRequest === "tool") result = await actor.rollToolCheck(config, dialog, message);
      else if (normalizedRequest === "deathSave") result = await actor.rollDeathSave(config, dialog, message);
      resolve(result ?? {});
    }, configSettings.playerSaveTimeout * 1e3);
    if (normalizedRequest === "save") result = await actor.rollSavingThrow(config, dialog, message);
    else if (normalizedRequest === "check") result = await actor.rollAbilityCheck(config, dialog, message);
    else if (normalizedRequest === "skill") result = await actor.rollSkill(config, dialog, message);
    else if (normalizedRequest === "tool") result = await actor.rollToolCheck(config, dialog, message);
    else if (normalizedRequest === "deathSave") result = await actor.rollDeathSave(config, dialog, message);
    if (timeoutId) clearTimeout(timeoutId);
    resolve(result ?? {});
  });
}
__name(rollAbility, "rollAbility");
function monksTokenBarSaves(data) {
  game.MonksTokenBar?.requestRoll(
    data.tokenData,
    {
      request: data.request,
      silent: data.silent,
      showdc: data.showdc,
      rollmode: data.rollMode,
      dc: data.dc
    }
  );
}
__name(monksTokenBarSaves, "monksTokenBarSaves");
function flashTokenBarSaves(data) {
  if (debugEnabled > 0) debug("flashTokenBarSaves | Called with data:", data);
  if (typeof FlashAPI !== "undefined") {
    if (debugEnabled > 0) debug("flashTokenBarSaves | FlashAPI found, calling requestRoll");
    FlashAPI.requestRoll({
      requestType: data.requestType,
      rollKey: data.rollKey,
      actorIds: data.actorIds,
      dc: data.dc,
      advantage: data.advantage,
      disadvantage: data.disadvantage,
      skipRollDialog: data.skipRollDialog,
      sendAsRequest: data.sendAsRequest,
      rollMode: data.rollMode,
      workflowId: data.workflowId
    });
  } else {
    error2("flashTokenBarSaves | FlashAPI not found! Is flash-rolls-5e module active?");
  }
}
__name(flashTokenBarSaves, "flashTokenBarSaves");
async function createReverseDamageCard(data) {
  let cardIds = [];
  let startTime;
  startTime = Date.now();
  const newDamageList = recoverDamageListFromJSON(data.damageList);
  let id = await createPlayerDamageCard({ ...data, damageList: newDamageList });
  if (id) cardIds.push(id);
  if (data.damageList.some((di) => di.wasHit)) {
    id = await createGMReverseDamageCard({ ...data, damageList: newDamageList }, true);
    if (id) cardIds.push(id);
  }
  if (data.damageList.some((di) => !di.wasHit) && ["yesCardMisses", "noCardMisses"].includes(data.autoApplyDamage)) {
    id = await createGMReverseDamageCard({ ...data, damageList: newDamageList }, false);
    if (id) cardIds.push(id);
  }
  console.log("createReverseDamageCard took ", Date.now() - startTime, "ms");
  return cardIds;
}
__name(createReverseDamageCard, "createReverseDamageCard");
function calculateDamageAmounts(damageDetail) {
  return damageDetail.reduce((acc, d) => {
    if (d.type === "temphp") acc.temp += d.value;
    else if (d.type === "vitality") acc.vitality += d.value;
    else if (d.type === "maximum") acc.tempMax += d.value;
    else if (d.type === "healing" || d.active?.absorption || d.active?.type?.absorption) acc.healing += d.value;
    else if (d.type !== "midi-none") acc.amount += d.value;
    return acc;
  }, { amount: 0, temp: 0, healing: 0, vitality: 0, tempMax: 0 });
}
__name(calculateDamageAmounts, "calculateDamageAmounts");
function updateDamageCardDisplay(html, actorUuid, undoEntry, showUndoMode) {
  const displayUuid = actorUuid.replaceAll(".", "");
  const { oldHP, newHP, oldTempHP, newTempHP, oldVitality, newVitality, oldTempMax, newTempMax } = undoEntry;
  const isHealing = showUndoMode;
  let fromHP, toHP, fromTempHP, toTempHP;
  let fromVitality, toVitality;
  let fromTempMax, toTempMax;
  if (showUndoMode) {
    const actor = fromActorUuid(actorUuid);
    fromHP = actor?.system.attributes.hp.value ?? newHP ?? 0;
    fromTempHP = actor?.system.attributes.hp.temp ?? newTempHP ?? 0;
    toHP = oldHP ?? 0;
    toTempHP = oldTempHP ?? 0;
    if (oldTempMax !== void 0) {
      fromTempMax = actor?.system.attributes.hp.tempmax ?? newTempMax ?? 0;
      toTempMax = oldTempMax;
    }
    const vitalityItem = getVitalityItem(actor);
    if (vitalityItem && oldVitality !== void 0) {
      const currentSpent = vitalityItem.system.uses?.spent ?? 0;
      const maxUses = vitalityItem.system.uses?.max ?? 0;
      fromVitality = maxUses - currentSpent;
      toVitality = oldVitality;
    }
  } else {
    fromHP = oldHP ?? 0;
    toHP = newHP ?? 0;
    fromTempHP = oldTempHP ?? 0;
    toTempHP = newTempHP ?? 0;
    fromVitality = oldVitality;
    toVitality = newVitality;
    fromTempMax = oldTempMax;
    toTempMax = newTempMax;
  }
  const hpDelta = Math.abs((toHP ?? 0) - (fromHP ?? 0));
  const tempHpDelta = Math.abs((toTempHP ?? 0) - (fromTempHP ?? 0));
  const vitalityDelta = Math.abs((toVitality ?? 0) - (fromVitality ?? 0));
  const tempMaxDelta = Math.abs((toTempMax ?? 0) - (fromTempMax ?? 0));
  const totalDamage = hpDelta + tempHpDelta;
  const hpChanged = hpDelta > 0;
  const tempHpChanged = tempHpDelta > 0;
  const vitalityChanged = vitalityDelta > 0;
  const tempMaxChanged = tempMaxDelta > 0;
  const noChange = !hpChanged && !tempHpChanged && !vitalityChanged && !tempMaxChanged;
  const totalSpan = html.querySelector(`#dmg-total-${displayUuid}`);
  const tempSpan = html.querySelector(`#dmg-temp-${displayUuid}`);
  const hpSpan = html.querySelector(`#dmg-hp-${displayUuid}`);
  const noChangeSpan = html.querySelector(`#dmg-nochange-${displayUuid}`);
  const vitalitySpan = html.querySelector(`#dmg-vitality-${displayUuid}`);
  const directVitalitySpan = html.querySelector(`#dmg-vit-direct-${displayUuid}`);
  const tempMaxSpan = html.querySelector(`#dmg-tempmax-${displayUuid}`);
  if (totalSpan) {
    const hasDamage = totalDamage > 0;
    totalSpan.classList.toggle("midi-qol-dmg-hidden", !hasDamage && noChange);
    totalSpan.classList.toggle("midi-qol-dmg-healing", isHealing);
    const signSpan = totalSpan.querySelector(".dmg-total-sign");
    if (signSpan) signSpan.textContent = isHealing ? "+" : "-";
    const valueSpan = totalSpan.querySelector(".dmg-total-value");
    if (valueSpan) valueSpan.textContent = String(Math.trunc(totalDamage));
  }
  if (directVitalitySpan) {
    directVitalitySpan.classList.toggle("midi-qol-dmg-hidden", !vitalityChanged);
    directVitalitySpan.classList.toggle("midi-qol-dmg-healing", isHealing);
    const signSpan = directVitalitySpan.querySelector(".dmg-vit-sign");
    if (signSpan) signSpan.textContent = isHealing ? "+" : "-";
    const valueSpan = directVitalitySpan.querySelector(".dmg-vit-value");
    if (valueSpan) valueSpan.textContent = String(Math.trunc(vitalityDelta));
  }
  if (tempSpan) {
    tempSpan.classList.toggle("midi-qol-dmg-hidden", !tempHpChanged);
    tempSpan.classList.toggle("midi-qol-dmg-healing", (toTempHP ?? 0) > (fromTempHP ?? 0));
    const tempOld = tempSpan.querySelector(".dmg-temp-old");
    const tempNew = tempSpan.querySelector(".dmg-temp-new");
    if (tempOld) tempOld.textContent = String(Math.trunc(fromTempHP ?? 0));
    if (tempNew) tempNew.textContent = String(Math.trunc(toTempHP ?? 0));
  }
  if (hpSpan) {
    hpSpan.classList.toggle("midi-qol-dmg-hidden", !hpChanged);
    hpSpan.classList.toggle("midi-qol-dmg-healing", isHealing);
    const hpOld = hpSpan.querySelector(".dmg-hp-old");
    const hpNew = hpSpan.querySelector(".dmg-hp-new");
    if (hpOld) hpOld.textContent = String(Math.trunc(fromHP ?? 0));
    if (hpNew) hpNew.textContent = String(Math.trunc(toHP ?? 0));
  }
  if (vitalitySpan) {
    if (vitalityChanged && fromVitality !== void 0 && toVitality !== void 0) {
      vitalitySpan.classList.remove("midi-qol-dmg-hidden");
      vitalitySpan.classList.toggle("midi-qol-dmg-healing", toVitality > fromVitality);
      const vitOld = vitalitySpan.querySelector(".dmg-vit-old");
      const vitNew = vitalitySpan.querySelector(".dmg-vit-new");
      if (vitOld) vitOld.textContent = String(Math.trunc(fromVitality));
      if (vitNew) vitNew.textContent = String(Math.trunc(toVitality));
    } else {
      vitalitySpan.classList.add("midi-qol-dmg-hidden");
    }
  }
  if (tempMaxSpan) {
    if (tempMaxChanged && fromTempMax !== void 0 && toTempMax !== void 0) {
      tempMaxSpan.classList.remove("midi-qol-dmg-hidden");
      const tmOld = tempMaxSpan.querySelector(".dmg-tempmax-old");
      const tmNew = tempMaxSpan.querySelector(".dmg-tempmax-new");
      if (tmOld) tmOld.textContent = String(Math.trunc(fromTempMax));
      if (tmNew) tmNew.textContent = String(Math.trunc(toTempMax));
    } else {
      tempMaxSpan.classList.add("midi-qol-dmg-hidden");
    }
  }
  if (noChangeSpan) {
    noChangeSpan.classList.toggle("midi-qol-dmg-hidden", !noChange);
  }
}
__name(updateDamageCardDisplay, "updateDamageCardDisplay");
function updateHPStatusHeader(html, actorUuid, applied) {
  if (actorUuid) {
    const displayUuid = actorUuid.replaceAll(".", "");
    const row = html.querySelector(`.midi-qol-dmg-row[data-actor-uuid="${displayUuid}"], .midi-qol-dmg-row-classic[data-actor-uuid="${displayUuid}"]`);
    if (row) {
      row.dataset.hpApplied = applied ? "true" : "false";
    }
  }
  const allRows = html.querySelectorAll(".midi-qol-dmg-row[data-actor-uuid], .midi-qol-dmg-row-classic[data-actor-uuid]");
  if (allRows.length === 0) return;
  let appliedCount = 0;
  let totalCount = 0;
  allRows.forEach((row) => {
    if (row.dataset.actorUuid) {
      totalCount++;
      if (row.dataset.hpApplied === "true") appliedCount++;
    }
  });
  const headerText = html.querySelector("#midi-qol-dmg-header .midi-qol-dmg-header-text");
  if (headerText) {
    if (appliedCount === 0) {
      headerText.textContent = i18n("midi-qol.HPNotUpdated");
    } else if (appliedCount === totalCount) {
      headerText.textContent = i18n("midi-qol.HPUpdated");
    } else {
      headerText.textContent = `${i18n("midi-qol.HPUpdated")} (${appliedCount}/${totalCount})`;
    }
  }
}
__name(updateHPStatusHeader, "updateHPStatusHeader");
function generateDamageTooltip(damageItem, tokenName) {
  const tooltipList = damageItem.damageDetail?.map((di) => {
    let allMods;
    if (isDnd5e53) {
      allMods = [];
      for (const cat of [
        "modification",
        "resistance",
        "vulnerability",
        "immunity",
        "absorption",
        "DR",
        "saved",
        "uncannyDodge",
        "spell",
        "magic",
        "nonmagic"
      ]) {
        if (di.active?.all?.[cat] || di.active?.type?.[cat]) allMods.push(cat);
      }
      if (di.active?.threshold) allMods.push("threshold");
    } else {
      allMods = Object.keys(di.active ?? {}).reduce((acc, k) => {
        if (k === "semiSuperSaver" || k === "superSaver" || k === "inlined") return acc;
        if (di.active?.[k] && k !== "multiplier") acc.push(k);
        return acc;
      }, []);
    }
    if ((di.allActives?.length ?? 0) > 0) allMods = allMods.concat(di.allActives);
    let mods = allMods.length > 0 ? `| ${allMods.join(",")}` : "";
    const displayVal = di.displayValue ?? di.value;
    return `${displayVal > 0 ? Math.floor(displayVal) : Math.ceil(displayVal)} ${{ ...GameSystemConfig.damageTypes, ...GameSystemConfig.healingTypes }[di.type === "" ? "none" : di.type]?.label ?? "none"} ${mods}`;
  }).map((s) => s.replaceAll(",,", ",").replaceAll(",,", ","));
  const toolTipHeader = [];
  if (damageItem.damageDetail) {
    if (damageItem.newHP !== damageItem.oldHP) toolTipHeader.push(`HP: ${damageItem.oldHP} -> ${damageItem.newHP}`);
    if ((damageItem.newTempHP ?? 0) !== (damageItem.oldTempHP ?? 0)) toolTipHeader.push(`TempHP: ${damageItem.oldTempHP} -> ${damageItem.newTempHP}`);
    if ((damageItem.newVitality ?? 0) !== (damageItem.oldVitality ?? 0)) toolTipHeader.push(`Vitality: ${damageItem.oldVitality} -> ${damageItem.newVitality}`);
    if ((damageItem.newTempMax ?? 0) !== (damageItem.oldTempMax ?? 0)) toolTipHeader.push(`Max HP: ${damageItem.oldTempMax} -> ${damageItem.newTempMax}`);
    if (damageItem.superSaver) toolTipHeader.push("Super Saved");
    else if (damageItem.semiSuperSaver) toolTipHeader.push("Semi Super Saved");
    else if (damageItem.saved) toolTipHeader.push("Saved");
    if (damageItem.uncannyDodge) toolTipHeader.push("Uncanny Dodge");
    if (damageItem.details?.length > 0) toolTipHeader.push(...damageItem.details);
  }
  const parts = [];
  if (tokenName) parts.push(`<b>${tokenName}</b>`);
  parts.push(...toolTipHeader ?? [], ...tooltipList ?? []);
  return parts.join("<br>");
}
__name(generateDamageTooltip, "generateDamageTooltip");
async function prepareDamageListItems(data, templateData, tokenDataList, createPromises = false, showNPC = true, doHits = true) {
  const damageList = data.damageList;
  let promises = [];
  for (let damageItem of damageList) {
    let { targetUuid, actorId, actorUuid, oldHP, newHP, hpDamage, wasHit } = damageItem;
    if (doHits && !wasHit) continue;
    if (!doHits && wasHit) continue;
    let tokenDocument = fromUuidSync(targetUuid);
    let actor = fromUuidSync(actorUuid);
    if (!actor) {
      if (debugEnabled > 0) warn(`GMAction: reverse damage card could not find actor to update HP targetUuid ${targetUuid} actorUuid ${actorUuid}`);
      continue;
    }
    if (!showNPC && !actor.hasPlayerOwner) continue;
    if (actor.isOwner && (data.autoApplyDamage !== "yesCardNPC" || actor.type !== "character")) {
      const hp = actor.system.attributes.hp;
      let { amount, temp, healing, vitality: vitalityDamage, tempMax } = calculateDamageAmounts(damageItem.damageDetail);
      amount = Math.max(0, amount);
      tempMax = Math.trunc(tempMax);
      const { amount: rawAmount, healing: rawHealing, vitality: rawVitality } = calculateDamageAmounts(damageItem.rawDamageDetail);
      damageItem.totalDamage = damageItem.damageDetail.reduce((acc, d) => acc + (!["temphp", "midi-none", "vitality", "maximum"].includes(d.type) ? d.value : 0), 0);
      amount = amount + healing;
      amount = amount > 0 ? Math.floor(amount) : Math.ceil(amount);
      let deltaTemp = amount > 0 ? Math.min(hp.temp, amount) : 0;
      let deltaHP = Math.clamp(amount - deltaTemp, -hp.damage + tempMax, hp.value - tempMax);
      if (hpDamage !== deltaHP && tempMax === 0) {
        error2(`damage detail amount ${amount} !== hpDamage ${hpDamage}`, configSettings.useDamageDetail ? "ignoring hpDamage" : "using hpDamage");
        if (!configSettings.useDamageDetail) deltaHP = hpDamage;
      }
      const updates = {
        // @ts-expect-error no dnd5e-types
        "system.attributes.hp.temp": hp.temp - deltaTemp,
        "system.attributes.hp.value": hp.value - deltaHP
      };
      if (tempMax !== 0) {
        updates["system.attributes.hp.tempmax"] = (hp.tempmax ?? 0) - tempMax;
      }
      if (temp > updates["system.attributes.hp.temp"]) updates["system.attributes.hp.temp"] = temp;
      damageItem.newTempHP = updates["system.attributes.hp.temp"];
      damageItem.newHP = updates["system.attributes.hp.value"];
      damageItem.oldTempMax = hp.tempmax ?? 0;
      damageItem.newTempMax = updates["system.attributes.hp.tempmax"] ?? damageItem.oldTempMax;
      damageItem.hpDamage = deltaHP;
      damageItem.tempDamage = deltaTemp;
      damageItem.rawTotalDamage = rawAmount + rawHealing + rawVitality;
      const vitalityItem = getVitalityItem(actor);
      let vitalityItemUpdatePromise;
      if (vitalityItem) {
        const currentSpent = vitalityItem.system.uses?.spent ?? 0;
        const maxUses = vitalityItem.system.uses?.max ?? 0;
        damageItem.oldVitalitySpent = currentSpent;
        damageItem.oldVitality = maxUses - currentSpent;
        damageItem.newVitalitySpent = currentSpent;
        damageItem.newVitality = maxUses - currentSpent;
        let newSpent = currentSpent;
        if (vitalityDamage !== 0) {
          if (vitalityDamage > 0) {
            newSpent = Math.min(maxUses, currentSpent + Math.floor(vitalityDamage));
          } else {
            newSpent = Math.max(0, currentSpent + Math.ceil(vitalityDamage));
          }
        }
        if (updates["system.attributes.hp.value"] <= 0 && amount > 0) {
          const overflowDamage = amount - (hp.temp + hp.value);
          if (overflowDamage > 0) {
            newSpent = Math.min(maxUses, newSpent + overflowDamage);
          }
        }
        if (newSpent !== currentSpent) {
          damageItem.newVitalitySpent = newSpent;
          damageItem.newVitality = maxUses - newSpent;
          if (createPromises && doHits && (data.autoApplyDamage.includes("yes") || data.forceApply)) {
            vitalityItemUpdatePromise = vitalityItem.update({ "system.uses.spent": newSpent });
          }
        }
      }
      if (createPromises && doHits && (data.autoApplyDamage.includes("yes") || data.forceApply)) {
        if (vitalityItemUpdatePromise) {
          damageItem.calcDamageOptions.midiVitalityHandled = true;
        }
        if (Hooks.call("dnd5e.preApplyDamage", actor, amount, updates, damageItem.calcDamageOptions) !== false) {
          const mergedOptions = foundry.utils.mergeObject(damageItem.calcDamageOptions, data.updateOptions ?? {}, { inplace: false });
          if (vitalityItemUpdatePromise) {
            mergedOptions.midiVitalityUpdatePending = true;
          }
          const updatePromise = actor.update(updates, mergedOptions).then(async (updatedActor) => {
            if (vitalityItemUpdatePromise) {
              await vitalityItemUpdatePromise;
            }
            if (updatedActor) Hooks.call("dnd5e.applyDamage", updatedActor, amount, damageItem.calcDamageOptions);
            return updatedActor;
          });
          promises.push(updatePromise);
        }
      }
    }
    tokenDataList.push({
      ...damageItem,
      targetUuid,
      actorUuid,
      actorId,
      totalDamage: Math.abs(Math.trunc(damageItem.totalDamage)),
      damageDetail: damageItem.rawDamageDetail,
      updateOptions: data.updateOptions ?? {}
    });
    let img = tokenDocument?.texture.src || actor.img;
    if (configSettings.usePlayerPortrait && actor.type === "character")
      img = actor.img;
    if (foundry.helpers.media.VideoHelper.hasVideoExtension(img)) {
      img = await game.video.createThumbnail(img, { width: 100, height: 100 });
    }
    const playerViewTotalDamage = damageItem.hpDamage + damageItem.tempDamage;
    const isHealing = (damageItem.rawTotalDamage ?? 0) < 0 || playerViewTotalDamage < 0;
    const vitalityEnabled = !!checkRule("vitalityItem");
    const directVitalityDamage = vitalityEnabled ? calculateDamageAmounts(damageItem.rawDamageDetail ?? damageItem.damageDetail ?? []).vitality : 0;
    let listItem = {
      isCharacter: actor.hasPlayerOwner,
      isNpc: !actor.hasPlayerOwner,
      actorUuid,
      targetUuid,
      displayUuid: actorUuid.replaceAll(".", ""),
      tokenId: tokenDocument?.id ?? void 0,
      tokenImg: img,
      hpDamage,
      abshpDamage: Math.abs(damageItem.hpDamage),
      tempDamage: (damageItem.newTempHP ?? 0) - (damageItem.oldTempHP ?? 0),
      totalDamage: Math.abs(Math.trunc(damageItem.totalDamage)),
      rawTotalDamage: damageItem.rawTotalDamage,
      halfDamage: Math.abs(Math.floor(damageItem.totalDamage / 2)),
      doubleDamage: Math.abs(damageItem.totalDamage * 2),
      playerViewTotalDamage,
      absDamage: Math.abs(damageItem.hpDamage),
      tokenName: tokenDocument?.name && configSettings.useTokenNames ? tokenDocument.name ?? actor.name : actor.name,
      dmgSign: damageItem.hpDamage < 0 ? "+" : "-",
      damageItem,
      oldVitality: damageItem.oldVitality,
      newVitality: damageItem.newVitality,
      vitalityIncrease: (damageItem.newVitality ?? 0) > (damageItem.oldVitality ?? 0),
      tempHpIncrease: (damageItem.newTempHP ?? 0) > (damageItem.oldTempHP ?? 0),
      hpChanged: newHP !== oldHP,
      tempHpChanged: (damageItem.newTempHP ?? 0) !== (damageItem.oldTempHP ?? 0),
      vitalityChanged: (damageItem.newVitality ?? 0) !== (damageItem.oldVitality ?? 0),
      tempMaxChanged: (damageItem.newTempMax ?? 0) !== (damageItem.oldTempMax ?? 0),
      noChange: newHP === oldHP && (damageItem.newTempHP ?? 0) === (damageItem.oldTempHP ?? 0) && (damageItem.newVitality ?? 0) === (damageItem.oldVitality ?? 0) && (damageItem.newTempMax ?? 0) === (damageItem.oldTempMax ?? 0),
      buttonId: actorUuid,
      // @ts-expect-error no dnd5e-types
      iconPrefix: data.autoApplyDamage === "yesCardNPC" && actor.type === "character" ? "*" : "",
      // Healing display properties
      isHealing,
      absRawTotalDamage: Math.abs(damageItem.rawTotalDamage ?? 0),
      absPlayerViewTotalDamage: Math.abs(playerViewTotalDamage),
      // Direct vitality damage/healing (not overflow)
      vitalityDamage: directVitalityDamage,
      absVitalityDamage: Math.abs(directVitalityDamage),
      vitalityDamageIsHealing: directVitalityDamage < 0
    };
    listItem.tooltip = generateDamageTooltip(damageItem, listItem.tokenName);
    templateData.damageList.push(listItem);
  }
  const allPromises = Promise.all(promises).then((allPromised) => {
    if (debugEnabled > 0) warn("GMAction.prepareDamageListItems: all promises completed", allPromised);
  });
  const promisesStart = Date.now();
  if (data.updateOptions?.awaitDamageApplication !== false) {
    await allPromises;
    debug("GMAction.prepareDamageListItems: awaited all promises", Date.now() - promisesStart);
  }
}
__name(prepareDamageListItems, "prepareDamageListItems");
async function createPlayerDamageCard(data) {
  let shouldShow = true;
  let chatCardUuid;
  if (configSettings.playerDamageCard === "none") return;
  if (configSettings.playerCardDamageDifferent) {
    shouldShow = false;
    for (let damageItem of data.damageList) {
      const { amount: rawAmount, healing: rawHealing } = calculateDamageAmounts(damageItem.rawDamageDetail);
      const rawTotal = rawAmount - rawHealing;
      if (rawTotal !== damageItem.hpDamage) {
        shouldShow = true;
        break;
      }
    }
  }
  if (!shouldShow) return;
  let showNPC = ["npcplayerresults", "npcplayerbuttons"].includes(configSettings.playerDamageCard);
  let playerButtons = ["playerbuttons", "npcplayerbuttons"].includes(configSettings.playerDamageCard);
  const damageList = data.damageList;
  let tokenDataList = [];
  let templateData = {
    damageApplied: ["yes", "yesCard", "yesCardMisses"].includes(data.autoApplyDamage) ? i18n("midi-qol.HPUpdated") : i18n("midi-qol.HPNotUpdated"),
    damageList: [],
    needsButtonAll: false,
    showNPC,
    playerButtons,
    healingLabel: getHealingLabel()
  };
  await prepareDamageListItems(data, templateData, tokenDataList, false, showNPC, true);
  if (templateData.damageList.length === 0) {
    log("No damage data to show to player");
    return;
  }
  templateData.needsButtonAll = damageList.length > 1;
  templateData.playerButtons = templateData.playerButtons && templateData.damageList.some((listItem) => listItem.isCharacter);
  if (["yesCard", "noCard", "yesCardNPC", "yesCardMisses", "noCardMisses"].includes(data.autoApplyDamage)) {
    const playerTemplate = configSettings.damageCardStyle === "classic" ? "modules/midi-qol/templates/damage-results-player-classic.html" : "modules/midi-qol/templates/damage-results-player.html";
    const content = await foundry.applications.handlebars.renderTemplate(playerTemplate, templateData);
    const speaker = ChatMessage.getSpeaker();
    speaker.alias = data.sender;
    let chatData = {
      user: game.user?.id,
      speaker: { scene: getCanvas()?.scene?.id, alias: data.charName, user: game.user?.id, actor: data.actorId },
      content,
      // whisper: ChatMessage.getWhisperRecipients("players").filter(u => u.active).map(u => u.id),
      flags: { "midi-qol": { "undoDamage": prepareDamageListToJSON(tokenDataList), playerDamageCard: true, actorUuid: data.actorUuid } }
    };
    chatData.style = CONST.CHAT_MESSAGE_STYLES.OTHER;
    if (data.flagTags) chatData.flags = foundry.utils.mergeObject(chatData.flags ?? "", data.flagTags);
    chatCardUuid = (await ChatMessage.create(chatData))?.uuid;
  }
  return chatCardUuid;
}
__name(createPlayerDamageCard, "createPlayerDamageCard");
async function createGMReverseDamageCard(data, doHits = true) {
  const damageList = data.damageList;
  let tokenDataList = [];
  let chatCardUuid;
  const damageWasApplied = doHits && (["yes", "yesCard", "yesCardMisses"].includes(data.autoApplyDamage) || data.forceApply);
  let templateData = {
    damageWasApplied,
    damageApplied: damageWasApplied ? i18n("midi-qol.HPUpdated") : data.autoApplyDamage === "yesCardNPC" ? i18n("midi-qol.HPNPCUpdated") : i18n("midi-qol.HPNotUpdated"),
    damageList: [],
    needsButtonAll: false
  };
  await prepareDamageListItems(data, templateData, tokenDataList, true, true, doHits);
  templateData.needsButtonAll = damageList.length > 1;
  if (["yesCard", "noCard", "yesCardNPC", "yesCardMisses", "noCardMisses"].includes(data.autoApplyDamage)) {
    const gmTemplate = configSettings.damageCardStyle === "classic" ? "modules/midi-qol/templates/damage-results-classic.html" : "modules/midi-qol/templates/damage-results.html";
    const content = await foundry.applications.handlebars.renderTemplate(gmTemplate, templateData);
    const speaker = ChatMessage.getSpeaker();
    speaker.alias = game.user?.name;
    let chatData = {
      speaker: { scene: getCanvas()?.scene?.id, alias: game.user?.name },
      content,
      whisper: ChatMessage.getWhisperRecipients("GM").filter((u) => u.active).map((u) => u.id),
      flags: { [MODULE_ID]: { "undoDamage": prepareDamageListToJSON(tokenDataList), actorUuid: data.actorUuid } }
    };
    chatData.style = CONST.CHAT_MESSAGE_STYLES.OTHER;
    if (data.flagTags) chatData.flags = foundry.utils.mergeObject(chatData.flags ?? {}, data.flagTags);
    chatCardUuid = (await ChatMessage.create(chatData))?.uuid;
  }
  return chatCardUuid;
}
__name(createGMReverseDamageCard, "createGMReverseDamageCard");
var processUndoDamageCard = /* @__PURE__ */ __name((message, html, _data) => {
  if (!message.flags?.[MODULE_ID]?.undoDamage) return true;
  let button = html.querySelector("#all-reverse");
  button?.addEventListener("click", (ev) => {
    (async () => {
      const undoFlag2 = message.flags?.[MODULE_ID]?.undoDamage ?? foundry.utils.getProperty(message, "flags.midi-qol.undoDamage");
      const undoDamageData2 = recoverDamageListFromJSON(undoFlag2);
      if (undoDamageData2) for (let { actorUuid, oldTempHP, oldHP, oldTempMax, newHP, newTempMax, calcDamageOptions, updateOptions } of undoDamageData2) {
        if (!actorUuid) continue;
        const applyButton = html.querySelector(`#apply-${actorUuid.replaceAll(".", "")}`);
        applyButton?.children[0].classList.add("midi-qol-enable-damage-button");
        applyButton?.children[0].classList.remove("midi-qol-disable-damage-button");
        const reverseButton = html.querySelector(`#reverse-${actorUuid.replaceAll(".", "")}`);
        reverseButton?.children[0].classList.remove("midi-qol-enable-damage-button");
        reverseButton?.children[0].classList.add("midi-qol-disable-damage-button");
        const multiplierSelect = html.querySelector(`#dmg-multiplier-${actorUuid.replaceAll(".", "")}`);
        if (multiplierSelect) multiplierSelect.value = "calc";
        let actor = fromActorUuid(actorUuid);
        const updates = {
          //@ts-expect-error no dnd5e-types
          "system.attributes.hp.temp": oldTempHP ?? 0,
          "system.attributes.hp.value": oldHP ?? 0
        };
        if (newTempMax - oldTempMax) updates["system.attributes.hp.tempmax"] = oldTempMax;
        const undoItem = undoDamageData2.find((d) => d.actorUuid === actorUuid);
        const vitalityItem = getVitalityItem(actor);
        let vitalityUpdated = false;
        if (vitalityItem && undoItem?.oldVitalitySpent !== void 0) {
          await vitalityItem.update({ "system.uses.spent": undoItem.oldVitalitySpent });
          vitalityUpdated = true;
        }
        if (actor?.isOwner && Hooks.call("dnd5e.preApplyDamage", actor, newHP - oldHP, updates, calcDamageOptions) !== false) {
          const mergedOptions = foundry.utils.mergeObject(calcDamageOptions, updateOptions ?? {}, { inplace: false });
          if (vitalityUpdated) mergedOptions.midiVitalityUpdatePending = true;
          await actor.update(updates, mergedOptions).then((updatedActor) => {
            Hooks.call("dnd5e.applyDamage", updatedActor ?? actor, newHP - oldHP, calcDamageOptions);
            if (undoItem) updateDamageCardDisplay(html, actorUuid, undoItem, false);
            updateHPStatusHeader(html, actorUuid, false);
            return updatedActor;
          });
        }
        ev.stopPropagation();
      }
    })();
  });
  button = html.querySelector("#all-apply");
  button?.addEventListener("click", (ev) => {
    (async () => {
      const undoFlag2 = message.flags?.[MODULE_ID]?.undoDamage ?? foundry.utils.getProperty(message, "flags.midi-qol.undoDamage");
      const undoDamageData2 = recoverDamageListFromJSON(undoFlag2);
      if (undoDamageData2) for (let { actorUuid, newHP, newTempHP, newTempMax, oldHP, oldTempMax, calcDamageOptions, updateOptions } of undoDamageData2) {
        if (!actorUuid) continue;
        let actor = fromActorUuid(actorUuid);
        const applyButton = html.querySelector(`#apply-${actorUuid.replaceAll(".", "")}`);
        applyButton?.children[0].classList.add("midi-qol-disable-damage-button");
        applyButton?.children[0].classList.remove("midi-qol-enable-damage-button");
        const reverseButton = html.querySelector(`#reverse-${actorUuid.replaceAll(".", "")}`);
        reverseButton?.children[0].classList.remove("midi-qol-disable-damage-button");
        reverseButton?.children[0].classList.add("midi-qol-enable-damage-button");
        log(`Setting HP to ${newTempHP} and ${newHP} ${actor?.name} ${actorUuid}`);
        const update = { "system.attributes.hp.temp": newTempHP ?? 0, "system.attributes.hp.value": newHP ?? 0 };
        if (newTempMax - oldTempMax) update["system.attributes.hp.tempmax"] = newTempMax;
        const amount = (newHP ?? 0) - (oldHP ?? 0);
        const applyItem = undoDamageData2.find((d) => d.actorUuid === actorUuid);
        const vitalityItem = getVitalityItem(actor);
        let vitalityUpdated = false;
        if (vitalityItem && applyItem?.newVitalitySpent !== void 0) {
          await vitalityItem.update({ "system.uses.spent": applyItem.newVitalitySpent });
          vitalityUpdated = true;
        }
        const options = foundry.utils.mergeObject(calcDamageOptions ?? {}, updateOptions ?? {}, { inplace: false });
        if (vitalityUpdated) options.midiVitalityUpdatePending = true;
        if (actor?.isOwner && Hooks.call("dnd5e.preApplyDamage", actor, amount, update, options) !== false) {
          await actor.update(update, options).then((updatedActor) => {
            Hooks.call("dnd5e.applyDamage", actor, amount, options);
            if (applyItem) updateDamageCardDisplay(html, actorUuid, applyItem, true);
            updateHPStatusHeader(html, actorUuid, true);
            return updatedActor;
          });
        }
        ev.stopPropagation();
      }
    })();
  });
  const undoFlag = message.flags?.[MODULE_ID]?.undoDamage ?? foundry.utils.getProperty(message, "flags.midi-qol.undoDamage");
  const undoDamageData = recoverDamageListFromJSON(undoFlag);
  undoDamageData?.forEach((undoEntry) => {
    const { actorUuid, oldTempHP, oldHP, oldTempMax, newHP, newTempHP, newTempMax, damageDetail, calcDamageOptions, updateOptions, oldVitalitySpent, newVitalitySpent } = undoEntry;
    if (!actorUuid) return;
    let button2 = html.querySelector(`#reverse-${actorUuid.replaceAll(".", "")}`);
    button2?.addEventListener("click", (ev) => {
      ev.currentTarget?.children[0].classList.add("midi-qol-disable-damage-button");
      ev.currentTarget?.children[0].classList.remove("midi-qol-enable-damage-button");
      const otherButton = html.querySelector(`#apply-${actorUuid.replaceAll(".", "")}`);
      otherButton?.children[0].classList.remove("midi-qol-disable-damage-button");
      otherButton?.children[0].classList.add("midi-qol-enable-damage-button");
      const multiplierSelect2 = html.querySelector(`#dmg-multiplier-${actorUuid.replaceAll(".", "")}`);
      if (multiplierSelect2) multiplierSelect2.value = "calc";
      (async () => {
        let actor = fromActorUuid(actorUuid);
        log(`Setting HP back to ${oldTempHP} and ${oldHP} ${actor?.name} ${actorUuid}`);
        const amount = newHP - oldHP;
        const updates = {
          //@ts-expect-error no dnd5e-types
          "system.attributes.hp.temp": oldTempHP ?? 0,
          "system.attributes.hp.value": oldHP ?? 0
        };
        if (newTempMax - oldTempMax) updates["system.attributes.hp.tempmax"] = oldTempMax;
        const vitalityItem = getVitalityItem(actor);
        let vitalityUpdated = false;
        if (vitalityItem && oldVitalitySpent !== void 0) {
          await vitalityItem.update({ "system.uses.spent": oldVitalitySpent });
          vitalityUpdated = true;
        }
        if (actor?.isOwner && Hooks.call("dnd5e.preApplyDamage", actor, amount, updates, calcDamageOptions) !== false) {
          const options = foundry.utils.mergeObject(calcDamageOptions ?? {}, updateOptions ?? {}, { inplace: false });
          if (vitalityUpdated) options.midiVitalityUpdatePending = true;
          await actor.update(updates, options).then((updatedActor) => {
            Hooks.call("dnd5e.applyDamage", actor, amount, calcDamageOptions);
            updateDamageCardDisplay(html, actorUuid, undoEntry, false);
            updateHPStatusHeader(html, actorUuid, false);
            return updatedActor;
          });
        }
        ev.stopPropagation();
      })();
    });
    button2 = html.querySelector(`#apply-${actorUuid.replaceAll(".", "")}`);
    button2?.addEventListener("click", (ev) => {
      ev.currentTarget?.children[0].classList.add("midi-qol-disable-damage-button");
      ev.currentTarget?.children[0].classList.remove("midi-qol-enable-damage-button");
      const otherButton = html.querySelector(`#reverse-${actorUuid.replaceAll(".", "")}`);
      otherButton?.children[0].classList.remove("midi-qol-disable-damage-button");
      otherButton?.children[0].classList.add("midi-qol-enable-damage-button");
      const multiplierSelect2 = html.querySelector(`#dmg-multiplier-${actorUuid.replaceAll(".", "")}`);
      let multiplierString = multiplierSelect2?.value;
      const mults = { "-1": -1, "x1": 1, "x0.25": 0.25, "x0.5": 0.5, "x2": 2 };
      (async () => {
        let actor = fromActorUuid(actorUuid);
        if (multiplierString === "calc") {
          const amount = (newHP ?? 0) - (oldHP ?? 0);
          const updates = {
            //@ts-expect-error no dnd5e-types
            "system.attributes.hp.temp": newTempHP ?? 0,
            "system.attributes.hp.value": newHP ?? 0
          };
          if (newTempMax - oldTempMax) updates["system.attributes.hp.tempmax"] = newTempMax;
          const vitalityItem = getVitalityItem(actor);
          let vitalityUpdated = false;
          if (vitalityItem && newVitalitySpent !== void 0) {
            await vitalityItem.update({ "system.uses.spent": newVitalitySpent });
            vitalityUpdated = true;
          }
          const options = foundry.utils.mergeObject(calcDamageOptions ?? {}, updateOptions ?? {}, { inplace: false });
          if (vitalityUpdated) options.midiVitalityUpdatePending = true;
          if (actor?.isOwner && Hooks.call("dnd5e.preApplyDamage", actor, amount, updates, calcDamageOptions) !== false) {
            await actor.update(updates, options).then((updatedActor) => {
              Hooks.call("dnd5e.applyDamage", actor, amount, calcDamageOptions);
              updateDamageCardDisplay(html, actorUuid, undoEntry, true);
              updateHPStatusHeader(html, actorUuid, true);
              return updatedActor;
            });
          }
        } else {
          const applyOptions = foundry.utils.mergeObject(updateOptions ?? {}, { multiplier: mults[multiplierString ?? "x1"] ?? 1 }, { inplace: false });
          const preApplyHP = actor?.system.attributes.hp.value ?? 0;
          const preApplyTempHP = actor?.system.attributes.hp.temp ?? 0;
          const hpDamageDetail = damageDetail?.filter((d) => d.type !== "vitality" && d.type !== "maximum") ?? [];
          const calculatedDamages = foundry.utils.deepClone(actor?.calculateDamage(hpDamageDetail, applyOptions) ?? []);
          let totalMultipliedHpDamage = 0;
          for (const dmg of calculatedDamages) {
            if (dmg.type !== "temphp" && !["midi-none", "none", "vitality"].includes(dmg.type)) {
              totalMultipliedHpDamage += dmg.value;
            }
          }
          totalMultipliedHpDamage = Math.trunc(totalMultipliedHpDamage);
          const multiplier = mults[multiplierString ?? "x1"] ?? 1;
          let multipliedVitalityDamage = 0;
          let multipliedTempMaxDamage = 0;
          for (const d of damageDetail ?? []) {
            if (d.type === "vitality") {
              multipliedVitalityDamage += d.value * multiplier;
            } else if (d.type === "maximum") {
              multipliedTempMaxDamage += d.value * multiplier;
            }
          }
          multipliedVitalityDamage = Math.trunc(multipliedVitalityDamage);
          multipliedTempMaxDamage = Math.trunc(multipliedTempMaxDamage);
          const vitalityItem = getVitalityItem(actor);
          const currentSpent = vitalityItem?.system.uses?.spent ?? 0;
          const maxUses = vitalityItem?.system.uses?.max ?? 0;
          let newSpent = currentSpent;
          if (vitalityItem) {
            if (multipliedVitalityDamage > 0) {
              newSpent = Math.min(maxUses, currentSpent + multipliedVitalityDamage);
            } else if (multipliedVitalityDamage < 0) {
              newSpent = Math.max(0, currentSpent + multipliedVitalityDamage);
            }
            if (totalMultipliedHpDamage > 0) {
              const tempAbsorbed = Math.min(preApplyTempHP, totalMultipliedHpDamage);
              const hpAbsorbed = Math.min(preApplyHP, totalMultipliedHpDamage - tempAbsorbed);
              const overflowDamage = totalMultipliedHpDamage - tempAbsorbed - hpAbsorbed;
              if (overflowDamage > 0) {
                newSpent = Math.min(maxUses, newSpent + overflowDamage);
              }
            } else if (totalMultipliedHpDamage < 0) {
              const healingAmount = Math.abs(totalMultipliedHpDamage);
              const vitalityMissing = newSpent;
              const vitalityRestored = Math.min(vitalityMissing, healingAmount);
              if (vitalityRestored > 0) {
                newSpent = newSpent - vitalityRestored;
                const remainingHealing = healingAmount - vitalityRestored;
                if (remainingHealing > 0) {
                  const scaleFactor = remainingHealing / healingAmount;
                  applyOptions.multiplier = (applyOptions.multiplier ?? 1) * scaleFactor;
                } else {
                  applyOptions.multiplier = 0;
                }
              }
            }
          }
          applyOptions.midiVitalityHandled = true;
          await actor?.applyDamage(hpDamageDetail, applyOptions);
          if (vitalityItem && newSpent !== currentSpent) {
            await vitalityItem.update({ "system.uses.spent": newSpent });
          }
          if (multipliedTempMaxDamage !== 0 && actor) {
            const currentTempMax = actor.system.attributes.hp.tempmax ?? 0;
            await actor.update({ "system.attributes.hp.tempmax": currentTempMax - multipliedTempMaxDamage });
          }
          updateDamageCardDisplay(html, actorUuid, undoEntry, true);
          updateHPStatusHeader(html, actorUuid, true);
        }
      })();
    });
    const displayUuidForSelect = actorUuid.replaceAll(".", "");
    const multiplierSelect = html.querySelector(`#dmg-multiplier-${displayUuidForSelect}`);
    multiplierSelect?.addEventListener("change", async (ev) => {
      const actor = fromActorUuid(actorUuid);
      if (!actor) return;
      const multiplierString = ev.target.value;
      const mults = { "-1": -1, "x1": 1, "x0.25": 0.25, "x0.5": 0.5, "x2": 2 };
      const displayUuid = actorUuid.replaceAll(".", "");
      const currentHP = actor.system.attributes.hp.value ?? 0;
      const currentTempHP = actor.system.attributes.hp.temp ?? 0;
      const maxHP = actor.system.attributes.hp.max ?? 0;
      let options;
      if (multiplierString === "calc") {
        options = foundry.utils.deepClone(calcDamageOptions ?? {});
      } else {
        options = { multiplier: mults[multiplierString ?? "x1"] ?? 1 };
      }
      const calculatedDamages = foundry.utils.deepClone(actor.calculateDamage(damageDetail, options));
      let totalDamage = 0;
      let tempHpDamage = 0;
      let multipliedVitalityDamage = 0;
      let multipliedTempMaxDamage = 0;
      for (const dmg of calculatedDamages) {
        if (dmg.type === "temphp") {
          tempHpDamage += dmg.value;
        } else if (dmg.type === "vitality") {
          multipliedVitalityDamage += dmg.value;
        } else if (dmg.type === "maximum") {
          multipliedTempMaxDamage += dmg.value;
        } else if (!["midi-none", "none"].includes(dmg.type)) {
          totalDamage += dmg.value;
        }
      }
      totalDamage = Math.trunc(totalDamage);
      tempHpDamage = Math.trunc(tempHpDamage);
      multipliedVitalityDamage = Math.trunc(multipliedVitalityDamage);
      multipliedTempMaxDamage = Math.trunc(multipliedTempMaxDamage);
      let newTempHP2 = currentTempHP;
      let newHP2 = currentHP;
      const isHealing = totalDamage < 0;
      if (tempHpDamage > 0) {
        newTempHP2 = Math.max(currentTempHP, tempHpDamage);
      }
      if (isHealing) {
        let healingRemaining = Math.abs(totalDamage);
        newHP2 = Math.min(maxHP, currentHP + healingRemaining);
      } else {
        let remainingDamage = totalDamage;
        if (currentTempHP > 0 && remainingDamage > 0) {
          const tempAbsorbed = Math.min(currentTempHP, remainingDamage);
          newTempHP2 = currentTempHP - tempAbsorbed;
          remainingDamage -= tempAbsorbed;
        }
        newHP2 = Math.max(0, currentHP - remainingDamage);
      }
      const totalSpan = html.querySelector(`#dmg-total-${displayUuid}`);
      const tempSpan = html.querySelector(`#dmg-temp-${displayUuid}`);
      const hpSpan = html.querySelector(`#dmg-hp-${displayUuid}`);
      const noChangeSpan = html.querySelector(`#dmg-nochange-${displayUuid}`);
      const tempMaxSpan = html.querySelector(`#dmg-tempmax-${displayUuid}`);
      const currentTempMax = actor.system.attributes.hp.tempmax ?? 0;
      const newTempMax2 = currentTempMax - multipliedTempMaxDamage;
      const tempMaxChanged = multipliedTempMaxDamage !== 0;
      const vitalityItem = getVitalityItem(actor);
      let currentVitality = 0;
      if (vitalityItem) {
        const currentSpent = vitalityItem.system.uses?.spent ?? 0;
        const maxUses = vitalityItem.system.uses?.max ?? 0;
        currentVitality = maxUses - currentSpent;
      }
      const maxAbsorbable = currentTempHP + currentHP + currentVitality;
      const cappedDamage = isHealing ? Math.abs(totalDamage) : Math.min(Math.abs(totalDamage), maxAbsorbable);
      const absTotalDamage = Math.trunc(cappedDamage);
      const hpChanged = newHP2 !== currentHP;
      const tempHpChanged = newTempHP2 !== currentTempHP;
      const hasDamageToApply = absTotalDamage > 0;
      const noChange = !hpChanged && !tempHpChanged && !hasDamageToApply && !tempMaxChanged;
      if (totalSpan) {
        totalSpan.classList.toggle("midi-qol-dmg-hidden", !hasDamageToApply);
        totalSpan.classList.toggle("midi-qol-dmg-healing", isHealing);
        totalSpan.textContent = `${isHealing ? "+" : "-"}${absTotalDamage}`;
      }
      if (tempSpan) {
        tempSpan.classList.toggle("midi-qol-dmg-hidden", !tempHpChanged);
        tempSpan.classList.toggle("midi-qol-dmg-healing", newTempHP2 > currentTempHP);
        const tempOld = tempSpan.querySelector(".dmg-temp-old");
        const tempNew = tempSpan.querySelector(".dmg-temp-new");
        if (tempOld) tempOld.textContent = String(Math.trunc(currentTempHP));
        if (tempNew) tempNew.textContent = String(Math.trunc(newTempHP2));
      }
      if (hpSpan) {
        hpSpan.classList.toggle("midi-qol-dmg-hidden", !hpChanged);
        hpSpan.classList.toggle("midi-qol-dmg-healing", isHealing);
        const hpOld = hpSpan.querySelector(".dmg-hp-old");
        const hpNew = hpSpan.querySelector(".dmg-hp-new");
        if (hpOld) hpOld.textContent = String(Math.trunc(currentHP));
        if (hpNew) hpNew.textContent = String(Math.trunc(newHP2));
      }
      if (tempMaxSpan) {
        tempMaxSpan.classList.toggle("midi-qol-dmg-hidden", !tempMaxChanged);
        const tmOld = tempMaxSpan.querySelector(".dmg-tempmax-old");
        const tmNew = tempMaxSpan.querySelector(".dmg-tempmax-new");
        if (tmOld) tmOld.textContent = String(Math.trunc(currentTempMax));
        if (tmNew) tmNew.textContent = String(Math.trunc(newTempMax2));
      }
      if (noChangeSpan) {
        noChangeSpan.classList.toggle("midi-qol-dmg-hidden", !noChange);
      }
      const vitalitySpan = html.querySelector(`#dmg-vitality-${displayUuid}`);
      const directVitalitySpan = html.querySelector(`#dmg-vit-direct-${displayUuid}`);
      const maxVitality = vitalityItem?.system.uses?.max ?? 0;
      if (vitalityItem) {
        let newVitality = currentVitality;
        if (multipliedVitalityDamage > 0) {
          newVitality = Math.max(0, currentVitality - multipliedVitalityDamage);
        } else if (multipliedVitalityDamage < 0) {
          newVitality = Math.min(maxVitality, currentVitality - multipliedVitalityDamage);
        }
        if (isHealing) {
          const healingAmount = Math.abs(totalDamage);
          const vitalityMissing = maxVitality - newVitality;
          const vitalityRestored = Math.min(vitalityMissing, healingAmount);
          newVitality = newVitality + vitalityRestored;
          const remainingHealing = healingAmount - vitalityRestored;
          newHP2 = Math.min(maxHP, currentHP + remainingHealing);
        } else if (newHP2 <= 0 && totalDamage > 0) {
          const tempAbsorbed = Math.min(currentTempHP, totalDamage);
          const hpAbsorbed = Math.min(currentHP, totalDamage - tempAbsorbed);
          const overflowDamage = totalDamage - tempAbsorbed - hpAbsorbed;
          if (overflowDamage > 0) {
            newVitality = Math.max(0, newVitality - overflowDamage);
          }
        }
        const vitalityChanged = newVitality !== currentVitality;
        const vitalityIncrease = newVitality > currentVitality;
        if (directVitalitySpan) {
          const hasDirectVitality = multipliedVitalityDamage !== 0;
          const directVitalityIsHealing = multipliedVitalityDamage < 0;
          directVitalitySpan.classList.toggle("midi-qol-dmg-hidden", !hasDirectVitality);
          directVitalitySpan.classList.toggle("midi-qol-dmg-healing", directVitalityIsHealing);
          const signSpan = directVitalitySpan.querySelector(".dmg-vit-sign");
          if (signSpan) signSpan.textContent = directVitalityIsHealing ? "+" : "-";
          const valueSpan = directVitalitySpan.querySelector(".dmg-vit-value");
          if (valueSpan) valueSpan.textContent = String(Math.abs(multipliedVitalityDamage));
        }
        if (vitalitySpan) {
          vitalitySpan.classList.toggle("midi-qol-dmg-hidden", !vitalityChanged);
          vitalitySpan.classList.toggle("midi-qol-dmg-healing", vitalityIncrease);
          const vitOld = vitalitySpan.querySelector(".dmg-vit-old");
          const vitNew = vitalitySpan.querySelector(".dmg-vit-new");
          if (vitOld) vitOld.textContent = String(Math.trunc(currentVitality));
          if (vitNew) vitNew.textContent = String(Math.trunc(newVitality));
        }
        if (isHealing && hpSpan) {
          const hpNew = hpSpan.querySelector(".dmg-hp-new");
          if (hpNew) hpNew.textContent = String(Math.trunc(newHP2));
          const hpChangedAfterVitality = newHP2 !== currentHP;
          hpSpan.classList.toggle("midi-qol-dmg-hidden", !hpChangedAfterVitality);
        }
      } else {
        if (vitalitySpan) vitalitySpan.classList.add("midi-qol-dmg-hidden");
        if (directVitalitySpan) directVitalitySpan.classList.add("midi-qol-dmg-hidden");
      }
    });
    const allTokenImgs = html.querySelectorAll(`img.midi-qol-target-name`);
    allTokenImgs.forEach((img) => {
      const parent = img.closest(".midi-qol-tooltip");
      if (!parent) return;
      const hasOurElements = parent.querySelector(`#dmg-total-${actorUuid.replaceAll(".", "")}`);
      if (!hasOurElements) return;
      img.addEventListener("click", (ev) => {
        ev.preventDefault();
        ev.stopImmediatePropagation();
        const tokenId = img.id;
        const token = canvas.tokens?.get(tokenId);
        if (token) {
          token.control({ releaseOthers: !ev.shiftKey });
          const center = token.center;
          canvas.animatePan({ x: center.x, y: center.y });
        }
      }, { capture: true });
      img.addEventListener("contextmenu", (ev) => {
        ev.preventDefault();
        ev.stopImmediatePropagation();
        const actor = fromActorUuid(actorUuid);
        if (actor) {
          actor.sheet?.render(true);
        }
      }, { capture: true });
    });
  });
  return true;
}, "processUndoDamageCard");
async function _moveToken(data) {
  const tokenDocument = fromUuidSync(data.tokenUuid);
  if (!tokenDocument || !canvas.grid || !tokenDocument.object) {
    const message = `moveToken | token not found or canvas not ready: ${data.tokenUuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("moveToken failed"), message);
    return;
  }
  const targetToken = tokenDocument.object;
  const { animate = true, teleport = false, ignoreWalls = false, ignoreTokens = false, autoRotate = true } = data.options ?? {};
  const snappedCenter = canvas.grid.getSnappedPoint(data.newCenter, { mode: CONST.GRID_SNAPPING_MODES.CENTER });
  const newPosition = { x: snappedCenter.x - targetToken.w / 2, y: snappedCenter.y - targetToken.h / 2 };
  if (data.newCenter.elevation !== void 0) newPosition.elevation = data.newCenter.elevation;
  const action = teleport ? "displace" : CONFIG.Token.movement.defaultAction;
  const waypoint = { ...newPosition, action, forced: true, snapped: true };
  const moveOptions = { constrainOptions: { ignoreWalls: teleport || ignoreWalls, ignoreCost: true, ignoreTokens: teleport || ignoreTokens }, autoRotate };
  if (teleport || !animate) moveOptions.animate = false;
  return tokenDocument.move(waypoint, moveOptions);
}
__name(_moveToken, "_moveToken");
async function _moveTokenAwayFromPoint(data) {
  const targetToken = getToken(data.targetUuid);
  const targetTokenDocument = getTokenDocument(targetToken);
  if (!canvas || !canvas.dimensions || !canvas.grid || !targetToken || !data.point || !targetTokenDocument) {
    const message = `moveTokenAwayFromPoint | token not found or canvas not ready: ${data.targetUuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("moveTokenAwayFromPoint failed"), message);
    return;
  }
  const ray = new foundry.canvas.geometry.Ray(data.point, targetToken.center);
  const pixelDistance = data.distance / canvas.dimensions.distance * canvas.dimensions.size;
  const newCenter = ray.project(1 + pixelDistance / ray.distance);
  return void _moveToken({ tokenUuid: targetTokenDocument.uuid, newCenter, options: data.options });
}
__name(_moveTokenAwayFromPoint, "_moveTokenAwayFromPoint");
async function _teleportToken(data) {
  const originToken = fromUuidSync(data.tokenUuid);
  if (!originToken?.object || !canvas.grid) {
    const message = `teleportToken | token not found or canvas not ready: ${data.tokenUuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("teleportToken failed"), message);
    return;
  }
  const dest = data.destination;
  const originScene = originToken.parent;
  if (!originScene) {
    const message = `teleportToken | token has no parent scene: ${data.tokenUuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("teleportToken failed"), message);
    return;
  }
  const destScene = dest.sceneUuid ? fromUuidSync(dest.sceneUuid) : originScene;
  if (!destScene) {
    const message = `teleportToken | destination scene not found: ${dest.sceneUuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("teleportToken failed"), message);
    return;
  }
  const sameScene = !dest.sceneUuid || destScene.id === originScene.id;
  if (sameScene) {
    await _moveToken({ tokenUuid: data.tokenUuid, newCenter: { x: dest.x, y: dest.y, elevation: dest.elevation }, options: { teleport: true } });
    return;
  }
  const targetToken = originToken.object;
  const snappedCenter = canvas.grid.getSnappedPoint({ x: dest.x, y: dest.y }, { mode: CONST.GRID_SNAPPING_MODES.CENTER });
  const topLeft = { x: snappedCenter.x - targetToken.w / 2, y: snappedCenter.y - targetToken.h / 2 };
  const tokenData = originToken.toObject();
  delete tokenData._id;
  tokenData.x = topLeft.x;
  tokenData.y = topLeft.y;
  if (dest.elevation !== void 0) tokenData.elevation = dest.elevation;
  const newToken = await TokenDocument.implementation.create(tokenData, { parent: destScene, keepId: false });
  if (!newToken) {
    const message = `teleportToken | failed to create token on destination scene: ${destScene.uuid}`;
    error2(message);
    TroubleShooter.recordError(new Error("teleportToken failed"), message);
    return;
  }
  for (const combat of game.combats ?? []) {
    const toUpdate = [];
    for (const combatant of combat.combatants) {
      if (combatant.sceneId === originScene.id && combatant.tokenId === originToken.id) {
        toUpdate.push({ _id: combatant.id, sceneId: destScene.id, tokenId: newToken.id });
      }
    }
    if (toUpdate.length) await combat.updateEmbeddedDocuments("Combatant", toUpdate);
  }
  await originToken.delete();
  const ownerUser = game.users?.find((u) => u.active && !u.isGM && u.character?.id === originToken.actorId);
  if (ownerUser && ownerUser.viewedScene === originScene.id) {
    game.socket.emit("pullToScene", destScene.id, ownerUser.id);
  }
}
__name(_teleportToken, "_teleportToken");
async function rollActionSave(data) {
  const { DialogV2: DialogV26 } = foundry.applications.api;
  const actor = fromActorUuid(data.actorUuid);
  const confirmed = await DialogV26.confirm({
    window: { title: `${actor?.name ?? ""}: ${data.effectName}` },
    content: `<p>${actor?.name} &mdash; use your action to roll against ${data.effectName}?</p>`,
    rejectClose: false
  });
  if (!confirmed) return void 0;
  return rollAbility({ saveDetails: data.saveDetails, displayOptions: data.displayOptions });
}
__name(rollActionSave, "rollActionSave");
function prepareDamageListToJSON(damageList) {
  const newDL = foundry.utils.deepClone(damageList).map((damageItem) => {
    const { downgrade, ignore } = damageItem.calcDamageOptions;
    let newDowngrade;
    if (downgrade instanceof Set) newDowngrade = Array.from(downgrade);
    else if (downgrade) newDowngrade = downgrade;
    let newIgnore;
    if (ignore === true) newIgnore = ignore;
    else if (ignore) {
      newIgnore = {};
      for (const [key, value] of Object.entries(ignore)) {
        if (value instanceof Set) newIgnore[key] = Array.from(value);
        else if (value) newIgnore[key] = value;
      }
    }
    const newDamageDetail = prepareDamageDetailToJSON(damageItem.damageDetail);
    const newRawDamageDetail = prepareDamageDetailToJSON(damageItem.rawDamageDetail);
    const newDamageDetails = Object.fromEntries(Object.entries(damageItem.damageDetails).filter((i) => i[0] !== "calcDamageOptions").map(([key, value]) => [key, prepareDamageDetailToJSON(value)]));
    const serializedEntry = {
      ...damageItem,
      calcDamageOptions: {
        ...damageItem.calcDamageOptions,
        downgrade: newDowngrade,
        ignore: newIgnore
      },
      damageDetail: newDamageDetail,
      rawDamageDetail: newRawDamageDetail,
      // Maybe some day I'll figure out the proper way to type this
      damageDetails: newDamageDetails
    };
    return serializedEntry;
  });
  return newDL;
}
__name(prepareDamageListToJSON, "prepareDamageListToJSON");
function prepareDamageDetailToJSON(damageDetail) {
  return (damageDetail ?? []).map((d) => ({ ...d, properties: Array.from(d.properties ?? []) }));
}
__name(prepareDamageDetailToJSON, "prepareDamageDetailToJSON");
function recoverDamageListFromJSON(damageList) {
  const newDL = foundry.utils.deepClone(damageList).map((damageItem) => {
    const { downgrade, ignore } = damageItem.calcDamageOptions;
    let newDowngrade;
    if (downgrade instanceof Array) newDowngrade = new Set(downgrade);
    else if (downgrade) newDowngrade = downgrade;
    let newIgnore;
    if (ignore === true) newIgnore = ignore;
    else if (ignore) {
      newIgnore = {};
      for (const [key, value] of Object.entries(ignore)) {
        if (value instanceof Array) newIgnore[key] = new Set(value);
        else if (value) newIgnore[key] = value;
      }
    }
    const newDamageDetail = recoverDamageDetailFromJSON(damageItem.damageDetail);
    const newRawDamageDetail = recoverDamageDetailFromJSON(damageItem.rawDamageDetail);
    const newDamageDetails = Object.fromEntries(Object.entries(damageItem.damageDetails).map(([key, value]) => [key, recoverDamageDetailFromJSON(value)]));
    const unSerializedEntry = {
      ...damageItem,
      calcDamageOptions: {
        ...damageItem.calcDamageOptions,
        downgrade: newDowngrade,
        ignore: newIgnore
      },
      damageDetail: newDamageDetail,
      rawDamageDetail: newRawDamageDetail,
      damageDetails: newDamageDetails
    };
    return unSerializedEntry;
  });
  return newDL;
}
__name(recoverDamageListFromJSON, "recoverDamageListFromJSON");
function recoverDamageDetailFromJSON(damageDetail) {
  return (damageDetail ?? []).map((d) => ({ ...d, properties: new Set(d.properties) }));
}
__name(recoverDamageDetailFromJSON, "recoverDamageDetailFromJSON");

// src/module/activities/SummonActivity.ts
var MidiSummonActivity;
var MidiSummonSheet;
function setupSummonActivity() {
  if (debugEnabled > 0) warn("MidiQOL | SummonActivity | setupSummonActivity | Called");
  MidiSummonSheet = defineMidiSummonSheetClass(game.system.applications.activity.SummonSheet);
  MidiSummonActivity = defineMidiSummonActivityClass(GameSystemConfig.activityTypes.summon.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.summon = { documentClass: MidiSummonActivity };
  } else {
    GameSystemConfig.activityTypes["midiSummon"] = { documentClass: MidiSummonActivity };
  }
  Hooks.on("dnd5e.summonToken", (activity, profile, tokenData, options) => {
    if (!activity.friendlySummon) return;
    const caster = getTokenDocument(activity.actor);
    if (caster) tokenData.disposition = caster.disposition;
  });
}
__name(setupSummonActivity, "setupSummonActivity");
var defineMidiSummonSheetClass = /* @__PURE__ */ __name((baseClass) => {
  return class MidiSummonSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiSummonSheet");
    }
  };
}, "defineMidiSummonSheetClass");
var defineMidiSummonActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiSummonActivity extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiSummonActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.SUMMON", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.SUMMON.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiSummonSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          dialog: ActivityClass.metadata.usage.dialog,
          actions: {
            // placeSummons: MidiSummonActivity.#placeSummons 
            // not doing this as it would require a physical copy of the dnd5e #placeSummons code
            // Downside is that the summons button on the activity card will not create a remove summoned creature effect since there is no workflow looked up
          }
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    static defineSchema() {
      return {
        ...super.defineSchema(),
        friendlySummon: new foundry.data.fields.BooleanField({ initial: false, name: "friendlySummon", required: false })
      };
    }
    get possibleOtherActivity() {
      return false;
    }
    get isTriggerableActivity() {
      return true;
    }
    async _triggerSubsequentActions(config, results) {
    }
    async getPlacement(token, profile, options) {
      if (options?.placements?.length) {
        if (this.creatureSizes.size) {
          const size = this.creatureSizes.has(options.creatureSize) ? options.creatureSize : this.creatureSizes.first();
          const config = CONFIG.DND5E.actorSizes[size];
          if (config) token = token.clone({ width: config.token ?? 1, height: config.token ?? 1 });
        }
        return options.placements.map((p, i) => ({
          prototypeToken: token,
          x: p.x,
          y: p.y,
          elevation: p.elevation ?? this.getUsageToken()?.elevation ?? 0,
          rotation: p.rotation ?? 0,
          index: { total: i, unique: i }
        }));
      }
      return super.getPlacement(token, profile, options);
    }
  };
}, "defineMidiSummonActivityClass");

// src/module/activities/TransformActivity.ts
var MidiTransformActivity;
var MidiTransformSheet;
function setupTransformActivity() {
  if (debugEnabled > 0) warn("MidiQOL | TransformActivity | setupTransformActivity | Called");
  MidiTransformSheet = defineMidiTransformSheetClass(game.system.applications.activity.TransformSheet);
  MidiTransformActivity = defineMidiTransformActivityClass(GameSystemConfig.activityTypes.transform.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.transform = { documentClass: MidiTransformActivity };
  } else {
    GameSystemConfig.activityTypes["midiTransform"] = { documentClass: MidiTransformActivity };
  }
  Hooks.on("dnd5e.TransformToken", (activity, profile, tokenData, options) => {
    if (!activity.friendlyTransform) return;
    const caster = getTokenDocument(activity.actor);
    if (caster) tokenData.disposition = caster.disposition;
  });
}
__name(setupTransformActivity, "setupTransformActivity");
var defineMidiTransformSheetClass = /* @__PURE__ */ __name((baseClass) => {
  return class MidiTransformSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiTransformSheet");
    }
  };
}, "defineMidiTransformSheetClass");
var defineMidiTransformActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiTransformActivity extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiTransformActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.TRANSFORM", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.TRANSFORM.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiTransformSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          dialog: ActivityClass.metadata.usage.dialog
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    async _finalizeUsage(config, results) {
      const profile = this.profiles.find((p) => p._id === config.transform?.profile);
      if (profile) {
        const uuid = !this.transform.mode ? profile.uuid : await this.queryActor(profile);
        if (uuid) {
          if (results.message instanceof ChatMessage) await results.message.setFlag("dnd5e", "transform.uuid", uuid);
          else foundry.utils.setProperty(results.message, "flags.dnd5e.transform.uuid", uuid);
        }
      }
      const grandParent = Object.getPrototypeOf(ActivityClass);
      await grandParent.prototype._finalizeUsage.call(this, config, results);
      return this.transformActor(config, results.message);
    }
    static async #transformActor(event, target, message) {
    }
    async transformActor(config, message) {
      const targets = Array.from(config.midiOptions?.targetsToUse ?? config.workflow?.targets ?? game.user?.targets ?? /* @__PURE__ */ new Set());
      if (!targets.length) {
        ui.notifications?.warn("DND5E.ActionWarningNoToken", { localize: true });
        return;
      }
      const profileId = message.getFlag("dnd5e", "transform.profile");
      const profile = this.profiles.find((p) => p._id === profileId) || this.profiles[0];
      const uuid = message.getFlag("dnd5e", "transform.uuid") ?? await this.queryActor(profile);
      const source = await fromUuid(uuid);
      if (!source) {
        ui.notifications?.warn("DND5E.TRANSFORM.Warning.SourceActor", { localize: true });
        return;
      }
      const transformedActorUuids = [];
      const transformedActors = [];
      for (const token of targets) {
        let actor = token instanceof Actor ? token : token.actor;
        if (actor.isPolymorphed) {
          const reverted = await actor.revertOriginalForm({ renderSheet: false });
          if (reverted instanceof TokenDocument && reverted.actor) {
            actor = reverted.actor;
          } else if (reverted instanceof Actor) {
            actor = reverted;
          } else {
            ui.notifications?.warn(i18nFormat2("midi-qol.TRANSFORM.AlreadyTransformed", { name: actor.name }));
            continue;
          }
        }
        const originalActorUuid = actor.uuid;
        const transformedTokens = await actor.transformInto(source, this.settings);
        if (transformedTokens) {
          transformedActorUuids.push(originalActorUuid);
          if (transformedTokens instanceof Array && transformedTokens[0]?.actor)
            transformedActors.push(transformedTokens[0].actor);
          else if (transformedTokens instanceof TokenDocument && transformedTokens.actor)
            transformedActors.push(transformedTokens.actor);
        }
      }
      if (config.workflow) {
        config.workflow.transformedActorUuids = transformedActorUuids;
        config.workflow.transformedActors = transformedActors;
      }
    }
    static defineSchema() {
      return {
        ...super.defineSchema()
      };
    }
    get possibleOtherActivity() {
      return false;
    }
    get isTriggerableActivity() {
      return true;
    }
    async _triggerSubsequentActions(config, results) {
    }
  };
}, "defineMidiTransformActivityClass");

// src/module/Workflow.ts
var shiftOnlyEvent = { shiftKey: true, altKey: false, ctrlKey: false, metaKey: false, type: "" };
var minAttackTotal = -100;
var WorkflowDataFlags = /* @__PURE__ */ ((WorkflowDataFlags2) => {
  WorkflowDataFlags2["ammunitionOnUseMacros"] = "ammunitionOnUseMacros";
  WorkflowDataFlags2["bonusDamageTotal"] = "bonusDamageTotal";
  WorkflowDataFlags2["currentAction"] = "currentAction";
  WorkflowDataFlags2["damageList"] = "damageList";
  WorkflowDataFlags2["inCombat"] = "inCombat";
  WorkflowDataFlags2["onUseMacros"] = "OnUseMacros";
  WorkflowDataFlags2["otherDamageTotal"] = "otherDamageTotal";
  WorkflowDataFlags2["saveAttribution"] = "saveAttribution";
  WorkflowDataFlags2["saveDisplayData"] = "saveDisplayData";
  WorkflowDataFlags2["targetDescriptors"] = "flags.dnd5e.targets";
  WorkflowDataFlags2["otherActivityConsumed"] = "use.consumed";
  WorkflowDataFlags2["otherActivityScaling"] = "use.otherScaling";
  return WorkflowDataFlags2;
})(WorkflowDataFlags || {});
function damageDetailToObject(damageDetail) {
  return (damageDetail ?? []).map((d) => ({
    ...d,
    properties: Array.from(d.properties ?? [])
  }));
}
__name(damageDetailToObject, "damageDetailToObject");
function damageDetailsFromObject(damageDetail) {
  return (damageDetail ?? []).map((d) => ({
    ...d,
    properties: new Set(d.properties ?? [])
  }));
}
__name(damageDetailsFromObject, "damageDetailsFromObject");
var PersistedFieldStore = class {
  static {
    __name(this, "PersistedFieldStore");
  }
  #cache = /* @__PURE__ */ new Map();
  #getFlags;
  #persist;
  #forcePersist;
  constructor(getFlags, persist, forcePersist) {
    this.#getFlags = getFlags;
    this.#persist = persist;
    this.#forcePersist = forcePersist;
  }
  scalar(key, dflt, legacyKey) {
    if (this.#cache.has(key)) return this.#cache.get(key);
    const flags = this.#getFlags();
    return flags?.[key] ?? (legacyKey ? flags?.[legacyKey] : void 0) ?? dflt;
  }
  setScalar(key, value) {
    if (value === void 0) this.#cache.delete(key);
    else this.#cache.set(key, value);
    this.#persist({ [key]: value });
  }
  // Token sets persist as uuid arrays (or {uuid, name} pairs for "targets") and
  // restore to Sets of canvas placeables. An empty restore returns an uncached
  // fresh Set so a later flag write is still picked up.
  tokenSet(key, namePairs = false) {
    if (this.#cache.has(key)) return this.#cache.get(key);
    const data = this.#getFlags()?.[key];
    if (data?.length) {
      const tokens = new Set(data.map((entry) => fromUuidSync(namePairs ? entry.uuid : entry)?.object).filter((t) => !!t));
      this.#cache.set(key, tokens);
      return tokens;
    }
    return /* @__PURE__ */ new Set();
  }
  setTokenSet(key, tokens, namePairs = false) {
    if (tokens === void 0) this.#cache.delete(key);
    else this.#cache.set(key, new Set(Array.from(tokens).map((t) => t instanceof TokenDocument ? t.object : t).filter((t) => !!t)));
    const targetDocuments = Array.from(tokens ?? []).map((t) => getTokenDocument(t)).filter((t) => !!t);
    this.#persist({ [key]: targetDocuments.map((t) => namePairs ? { uuid: t.uuid, name: t.name } : t.uuid) });
  }
  // Rolls persist as toJSON() data and must be rehydrated with Roll.fromData
  // (which redirects to the persisted subclass via data.class). Falls back to
  // the raw data if the class is no longer registered.
  #rollFromData(data) {
    if (data instanceof Roll) return data;
    try {
      return Roll.fromData(data);
    } catch (err) {
      TroubleShooter.recordError(err, `PersistedFieldStore | could not rehydrate roll ${data?.class}`);
      return data;
    }
  }
  roll(key) {
    if (this.#cache.has(key)) return this.#cache.get(key);
    const data = this.#getFlags()?.[key];
    if (data) {
      const roll = this.#rollFromData(data);
      this.#cache.set(key, roll);
      return roll;
    }
    return void 0;
  }
  setRoll(key, roll) {
    if (roll === void 0) this.#cache.delete(key);
    else this.#cache.set(key, roll);
    this.#forcePersist({ [key]: roll?.toJSON() ?? null });
  }
  rolls(key) {
    if (this.#cache.has(key)) return this.#cache.get(key);
    const data = this.#getFlags()?.[key];
    if (data?.length) {
      const rolls = data.map((r) => this.#rollFromData(r));
      this.#cache.set(key, rolls);
      return rolls;
    }
    return void 0;
  }
  setRolls(key, rolls) {
    if (rolls === void 0) this.#cache.delete(key);
    else this.#cache.set(key, rolls);
    this.#forcePersist({ [key]: rolls?.map((r) => r.toJSON()) ?? [] });
  }
  damageDetail(key) {
    if (this.#cache.has(key)) return this.#cache.get(key);
    const data = this.#getFlags()?.[key];
    if (data?.length) {
      const detail = damageDetailsFromObject(data);
      this.#cache.set(key, detail);
      return detail;
    }
    return [];
  }
  setDamageDetail(key, detail) {
    if (detail === void 0) this.#cache.delete(key);
    else this.#cache.set(key, detail);
    this.#persist({ [key]: damageDetailToObject(detail) });
  }
};
var Workflow = class _Workflow {
  static {
    __name(this, "Workflow");
  }
  static WorkflowDataFlags = WorkflowDataFlags;
  static get forceCreate() {
    return true;
  }
  static #workflows = /* @__PURE__ */ new Map();
  static get workflows() {
    return _Workflow.#workflows;
  }
  static set workflows(workflows) {
    _Workflow.#workflows = workflows;
  }
  static clearWorkflows() {
    _Workflow.workflows = /* @__PURE__ */ new Map();
  }
  static addWorkflow(workflow) {
    if (_Workflow.workflows.has(workflow.id)) console.warn("midi-qol | addWorkflow | Workflow already exists", workflow.id);
    if (_Workflow.workflows.has(workflow.id)) _Workflow.removeWorkflow(workflow.id);
    if (useWeakReferences) _Workflow.workflows.set(workflow.id, new WeakRef(workflow));
    else _Workflow.workflows.set(workflow.id, workflow);
  }
  static getWorkflow(id) {
    if (debugEnabled > 0) warn("Get workflow ", id, _Workflow.workflows, _Workflow.workflows.get(id ?? ""));
    if (debugEnabled > 1) debug("Get workflow ", id, _Workflow.workflows, _Workflow.workflows.get(id ?? ""));
    if (!id) return;
    let workflow = _Workflow.workflows.get(id);
    if (workflow instanceof WeakRef) {
      if (!workflow?.deref()) {
        console.warn("Weak Reference to workflow is not dereferenced", id);
        const _workflow = _Workflow.fromChatCardUuid(id);
        if (_workflow) {
          workflow = new WeakRef(_workflow);
          _Workflow.workflows.set(id, workflow);
        }
        return _workflow;
      }
      return workflow.deref();
    } else if (workflow) return workflow;
    const chatCard = fromUuidSync(id);
    if (chatCard instanceof ChatMessage && saveToChatCard) {
      workflow = _Workflow.fromChatCardUuid(id);
      if (workflow) _Workflow.workflows.set(id, workflow);
      return workflow;
    }
    return this.getWorkflowByActivityUuid(id);
  }
  static getWorkflowByActivityUuid(activityUuid) {
    if (!activityUuid) return;
    const entry = _Workflow.workflows.get(activityUuid);
    let returnValue;
    if (entry instanceof WeakRef) {
      returnValue = entry.deref();
    } else {
      returnValue = entry;
    }
    if (debugEnabled > 0) warn(`Fetching workflow by activity uuid ${activityUuid} workflowId: ${returnValue?.id}`);
    return returnValue;
  }
  static deleteWorkflow(id) {
    _Workflow.workflows.delete(id);
  }
  static fromChatCardUuid(chatCardUuid) {
    return new _Workflow(null, null, null, null, { chatCardUuid });
  }
  async performThrottledUpdate(chatCard, updates, immediate = false) {
    return await throttledUpdate(chatCard, updates, this.throttleChatMessageUpdates, immediate);
  }
  itemCardUuid;
  /**
   * Direct access to chat card's midi-qol flags (no intermediate store).
   * Returns undefined when there is no chat card.
   */
  get midiFlags() {
    return this.chatCard?.flags?.[MODULE_ID];
  }
  /**
   * Persist field updates to the chat card flag cache.
   */
  persistFields(updates) {
    if (this.itemCardUuid && saveToChatCard) {
      addUpdatesCacheFlags(this.id, updates, "midi-qol");
    }
  }
  /**
   * Persist roll data to the chat card flags unconditionally (regardless of saveToChatCard).
   * ChatMessageMidi.prepareDerivedData() deserializes these flags into Roll instances.
   */
  forcePersistFields(updates) {
    if (this.itemCardUuid) {
      addUpdatesCacheFlags(this.id, updates, "midi-qol");
    }
  }
  #persisted = new PersistedFieldStore(() => this.midiFlags, (updates) => this.persistFields(updates), (updates) => this.forcePersistFields(updates));
  get useActiveDefence() {
    return false;
  }
  get actor() {
    return this.activity?.actor;
  }
  get abort() {
    return this.#persisted.scalar("abort", false, "aborted");
  }
  set abort(abort) {
    this.#persisted.setScalar("abort", !!abort);
  }
  get itemUseComplete() {
    return this.#persisted.scalar("itemUseComplete", false);
  }
  set itemUseComplete(itemUseComplete) {
    this.#persisted.setScalar("itemUseComplete", !!itemUseComplete);
  }
  #token;
  get token() {
    if (this.#token) return this.#token;
    const uuid = this.midiFlags?.attackingTokenUuid;
    if (uuid) {
      this.#token = getToken(uuid);
    }
    return this.#token;
  }
  set token(token) {
    this.#token = token;
    this.persistFields({ attackingTokenUuid: token?.document?.uuid ?? "" });
  }
  get workflowOptions() {
    return this.#persisted.scalar("workflowOptions", {});
  }
  set workflowOptions(workflowOptions) {
    this.#persisted.setScalar("workflowOptions", workflowOptions);
  }
  get needItemCard() {
    return !!this.itemCardUuid;
  }
  #activity;
  get activity() {
    if (this.#activity) return this.#activity;
    if (this.chatCard) {
      this.#activity = this.chatCard.getAssociatedActivity();
      if (this.#activity) {
        const scaling = this.chatCard.getFlag("dnd5e", "scaling");
        if (scaling) {
          const item = this.#activity.item.clone({ flags: { dnd5e: { scaling } } }, { keepId: true });
          this.#activity = item.system.activities.get(this.#activity.id);
        }
      }
    }
    if (!this.#activity) {
      const activityUuid = this.midiFlags?.activityUuid;
      if (activityUuid) {
        this.#activity = fromUuidSync(activityUuid);
      }
    }
    if (!this.#activity) {
      const message = `midi-qol | get activity() | Could not find activity for workflow ${this.id}`;
      TroubleShooter.recordError(new Error(message));
      error2(message);
    }
    return this.#activity;
  }
  set activity(activity) {
    this.#activity = activity;
    this.persistFields({ activityUuid: activity?.uuid ?? "" });
  }
  #item;
  get item() {
    if (this.#item) return this.#item;
    if (this.itemCardUuid && saveToChatCard) {
      const itemUuid = this.chatCard?.getFlag("dnd5e", "item")?.uuid;
      this.#item = fromUuidSync(itemUuid);
      if (!this.#item) {
        const message = `get item() | Could not find item for workflow ${this.id} with itemUuid ${itemUuid}`;
        TroubleShooter.recordError(new Error(message));
        error2(message);
      }
    }
    return this.#item;
  }
  set item(item) {
    this.#item = item;
    if (this.itemCardUuid && saveToChatCard) {
      addUpdatesCacheFlags(this.id, { ["dnd5e.item.uuid"]: item?.uuid });
    }
  }
  // This value is not stored in flags
  #speaker = void 0;
  get speaker() {
    if (this.#speaker) return this.#speaker;
    if (this.itemCardUuid && saveToChatCard) {
      this.#speaker = this.chatCard.speaker;
    }
    return this.#speaker;
  }
  set speaker(speaker) {
    this.#speaker = speaker;
    if (this.itemCardUuid && saveToChatCard) {
      addUpdatesCache(this.id, { speaker });
    }
  }
  get attackingToken() {
    return this.token;
  }
  get tokenUuid() {
    return this.token?.document.uuid;
  }
  get targets() {
    return this.#persisted.tokenSet("targets", true);
  }
  set targets(targets) {
    this.#persisted.setTokenSet("targets", targets ?? /* @__PURE__ */ new Set(), true);
  }
  get targetsCanSee() {
    return this.#persisted.tokenSet("targetsCanSeeUuids");
  }
  set targetsCanSee(targetsCanSee) {
    this.#persisted.setTokenSet("targetsCanSeeUuids", targetsCanSee);
  }
  get targetsCanSense() {
    return this.#persisted.tokenSet("targetsCanSenseUuids");
  }
  set targetsCanSense(targetsCanSense) {
    this.#persisted.setTokenSet("targetsCanSenseUuids", targetsCanSense);
  }
  get tokenCanSee() {
    return this.#persisted.tokenSet("tokenCanSeeUuids");
  }
  set tokenCanSee(tokenCanSee) {
    this.#persisted.setTokenSet("tokenCanSeeUuids", tokenCanSee);
  }
  get tokenCanSense() {
    return this.#persisted.tokenSet("tokenCanSenseUuids");
  }
  set tokenCanSense(tokenCanSense) {
    this.#persisted.setTokenSet("tokenCanSenseUuids", tokenCanSense);
  }
  templateUuids = [];
  expectedTemplateCount = 0;
  placedTemplateCount = 0;
  // Backward compat: templateUuid returns the first template UUID
  get templateUuid() {
    if (this.templateUuids.length > 0) return this.templateUuids[0];
    return this.midiFlags?.templateUuid ?? "";
  }
  set templateUuid(templateUuid) {
    if (templateUuid && !this.templateUuids.includes(templateUuid)) {
      this.templateUuids.push(templateUuid);
    }
    this.persistFields({ templateUuid, templateUuids: this.templateUuids });
  }
  get template() {
    return this.templateUuid ? fromUuidSync(this.templateUuid) : void 0;
  }
  set template(template) {
    if (template?.uuid) this.templateUuid = template.uuid;
  }
  get templateId() {
    if (!this.templateUuid || typeof this.templateUuid !== "string") return;
    return this.templateUuid.split(".").at(-1);
  }
  get inCombat() {
    return this.actor.inCombat;
  }
  isTurn;
  // Is it the item wielder's turn.
  get AoO() {
    return this.#persisted.scalar("AoO", false);
  }
  set AoO(AoO) {
    this.#persisted.setScalar("AoO", AoO);
  }
  #damageList;
  get damageList() {
    if (this.#damageList) return this.#damageList;
    return this.#damageList ?? [];
  }
  set damageList(damageList) {
    this.#damageList = damageList;
  }
  saveDisplayFlavor;
  #id = "";
  get id() {
    return this.#id;
  }
  set id(id) {
    this.#id = id;
  }
  get uuid() {
    if (debugEnabled > 0) warn(`workflow.uuid is deprecated. Use workflow.id to reference the workflow and workflow.itemUuid to reference the item instead.
      Returning ${this.activity.uuid} workflowId: ${this.id}`);
    return this.activity.uuid;
  }
  get suspended() {
    return this.#persisted.scalar("suspended", false);
  }
  set suspended(suspended) {
    this.#persisted.setScalar("suspended", suspended);
  }
  #currentAction;
  get currentAction() {
    if (this.#currentAction) return this.#currentAction;
    if (this.itemCardUuid && saveToChatCard) {
      const [className, currentActionName] = this.chatCard?.getFlag(MODULE_ID, "currentAction" /* currentAction */) ?? ["none", "none"];
      if (className !== "none") {
        const actionClass = WorkflowRegistry[className];
        if (actionClass && (actionClass === _Workflow || actionClass.prototype instanceof _Workflow)) {
          const action = actionClass?.prototype[currentActionName];
          if (action) {
            this.#currentAction = action;
          } else {
            error2(`get currentAction() | Could not find action ${currentActionName} in class ${className}`);
          }
        } else error2(`get currentAction() | Class ${className} is not a subclass of Workflow`);
      }
    }
    return this.#currentAction;
  }
  set currentAction(currentAction) {
    this.#currentAction = currentAction;
    if (this.itemCardUuid && saveToChatCard) {
      addUpdatesCacheFlags(this.id, { ["currentAction" /* currentAction */]: [this.constructor.name, currentAction?.name] }, "midi-qol");
    }
  }
  #isCriticalRestored = false;
  /**
   * Get computed critical state from the tracker.
   * On first access, restores from store (loaded from chat card flags) if available.
   */
  get isCritical() {
    if (!this.#isCriticalRestored && this.itemCardUuid && saveToChatCard) {
      if (this.midiFlags?.isCritical) {
        this.attackRollModifierTracker.critical.setOverride(true, "restored", "Restored from chat card");
      }
      this.#isCriticalRestored = true;
    }
    return this.attackRollModifierTracker.isCritical;
  }
  /**
   * Set critical state via override. Used for persistence and external assignment.
   * Prefer using tracker methods directly for attribution.
   */
  set isCritical(isCritical) {
    this.attackRollModifierTracker.critical.setOverride(isCritical, "direct", "Direct assignment");
    this.#isCriticalRestored = true;
    this.persistFields({ isCritical });
  }
  #isFumbleRestored = false;
  /**
   * Get computed fumble state from the tracker.
   * On first access, restores from store (loaded from chat card flags) if available.
   */
  get isFumble() {
    if (!this.#isFumbleRestored && this.itemCardUuid && saveToChatCard) {
      if (this.midiFlags?.isFumble) {
        this.attackRollModifierTracker.fumble.setOverride(true, "restored", "Restored from chat card");
      }
      this.#isFumbleRestored = true;
    }
    return this.attackRollModifierTracker.isFumble;
  }
  /**
   * Set fumble state via override. Used for persistence and external assignment.
   * Prefer using tracker methods directly for attribution.
   */
  set isFumble(isFumble) {
    this.attackRollModifierTracker.fumble.setOverride(isFumble, "direct", "Direct assignment");
    this.#isFumbleRestored = true;
    this.persistFields({ isFumble });
  }
  /**
   * @deprecated Use workflow.attackRollModifierTracker.critical.isForced instead
   */
  get critFlagSet() {
    foundry.utils.logCompatibilityWarning(
      "midi-qol | workflow.critFlagSet is deprecated. Use workflow.attackRollModifierTracker.critical.isForced instead.",
      { since: "13.0.40", until: "14", once: true }
    );
    return this.attackRollModifierTracker.critical.isForced;
  }
  /**
   * @deprecated Use workflow.attackRollModifierTracker.critical.isSuppressed instead
   */
  get noCritFlagSet() {
    foundry.utils.logCompatibilityWarning(
      "midi-qol | workflow.noCritFlagSet is deprecated. Use workflow.attackRollModifierTracker.critical.isSuppressed instead.",
      { since: "13.0.40", until: "14", once: true }
    );
    return this.attackRollModifierTracker.critical.isSuppressed;
  }
  get d20AttackRoll() {
    return this.#persisted.scalar("d20AttackRoll", -1);
  }
  set d20AttackRoll(d20AttackRoll) {
    this.#persisted.setScalar("d20AttackRoll", d20AttackRoll);
  }
  get hitTargets() {
    return this.#persisted.tokenSet("hitTargetUuids");
  }
  set hitTargets(hitTargets) {
    this.#persisted.setTokenSet("hitTargetUuids", hitTargets);
  }
  get hitTargetsEC() {
    return this.#persisted.tokenSet("hitECTargetUuids");
  }
  set hitTargetsEC(hitTargetsEC) {
    this.#persisted.setTokenSet("hitECTargetUuids", hitTargetsEC);
  }
  #otherDamageMatches;
  get otherDamageMatches() {
    if (this.#otherDamageMatches !== void 0) return this.#otherDamageMatches;
    const uuids = this.midiFlags?.otherDamageMatchUuids;
    if (uuids) {
      this.#otherDamageMatches = new Set(
        uuids.map((uuid) => fromUuidSync(uuid)?.object).filter((t) => !!t)
      );
      return this.#otherDamageMatches;
    }
    return void 0;
  }
  set otherDamageMatches(matches) {
    this.#otherDamageMatches = matches;
    const uuids = matches ? Array.from(matches).map((t) => getTokenDocument(t)?.uuid).filter((u) => !!u) : [];
    this.persistFields({ otherDamageMatchUuids: uuids });
  }
  get attackRoll() {
    return this.#persisted.roll("attackRoll");
  }
  set attackRoll(attackRoll) {
    if (attackRoll) attackRoll.data = {};
    this.#persisted.setRoll("attackRoll", attackRoll);
  }
  get diceRoll() {
    return this.#persisted.scalar("diceRoll", 0);
  }
  set diceRoll(diceRoll) {
    this.#persisted.setScalar("diceRoll", diceRoll);
  }
  get attackTotal() {
    return this.#persisted.scalar("attackTotal", 0);
  }
  set attackTotal(attackTotal) {
    this.#persisted.setScalar("attackTotal", attackTotal);
  }
  attackRollHTML;
  get attackRollCount() {
    return this.#persisted.scalar("attackRollCount", 0);
  }
  set attackRollCount(attackRollCount) {
    this.#persisted.setScalar("attackRollCount", attackRollCount);
  }
  // ─────────────────────────────────────────────────────────────────
  // Attack Advantage Mode - tracks advantage/disadvantage with counts and suppression
  // ─────────────────────────────────────────────────────────────────
  #attackRollModifierTracker = new RollModifierTracker();
  #attackRollModifierTrackerRestored = false;
  /**
   * Get the RollModifierTracker instance for attack rolls.
   * Use this for granular control over advantage/disadvantage tracking.
   * On first access, restores from chat card flags if available.
   */
  get attackRollModifierTracker() {
    if (!this.#attackRollModifierTrackerRestored && this.itemCardUuid && saveToChatCard) {
      const data = this.midiFlags?.attackRollModifierTrackerData;
      if (data) {
        this.#attackRollModifierTracker.restore(data);
        this.#isCriticalRestored = true;
        this.#isFumbleRestored = true;
      }
      this.#attackRollModifierTrackerRestored = true;
    }
    return this.#attackRollModifierTracker;
  }
  /** Alias for attackRollModifierTracker */
  get tracker() {
    return this.attackRollModifierTracker;
  }
  /**
   * Persist the attack roll modifier tracker state to chat card flags.
   * Called after processAttackRoll and checkHits when the tracker state is finalized.
   */
  persistAttackTracker() {
    this.persistFields({ attackRollModifierTrackerData: this.#attackRollModifierTracker.toJSON() });
  }
  /**
   * Backward compatibility getter for advantage.
   * Returns true if there's effective advantage, false if suppressed, undefined if no sources.
   */
  get advantage() {
    return this.#attackRollModifierTracker.hasAdvantage;
  }
  /**
   * Backward compatibility setter for advantage.
   * - `true`: adds one source of advantage
   * - `false`: clears all advantage sources (preserves suppression)
   * - `undefined`: resets the entire advantage mode state
   */
  set advantage(value) {
    if (value === true) {
      this.#attackRollModifierTracker.advantage.add();
    } else if (value === void 0) {
      this.#attackRollModifierTracker.reset();
    } else if (value === false) {
      this.#attackRollModifierTracker.advantage.clear();
    }
  }
  /**
   * Backward compatibility getter for disadvantage.
   * Returns true if there's effective disadvantage, false if suppressed, undefined if no sources.
   */
  get disadvantage() {
    return this.#attackRollModifierTracker.hasDisadvantage;
  }
  /**
   * Backward compatibility setter for disadvantage.
   * - `true`: adds one source of disadvantage
   * - `false`: clears all disadvantage sources (preserves suppression)
   * - `undefined`: resets the entire advantage mode state
   */
  set disadvantage(value) {
    if (value === true) {
      this.#attackRollModifierTracker.disadvantage.add();
    } else if (value === void 0) {
      this.#attackRollModifierTracker.reset();
    } else if (value === false) {
      this.#attackRollModifierTracker.disadvantage.clear();
    }
  }
  /**
   * Backward compatibility getter for noAdvantage.
   * Returns true if advantage is suppressed.
   */
  get noAdvantage() {
    return this.#attackRollModifierTracker.advantage.isSuppressed;
  }
  /**
   * Backward compatibility setter for noAdvantage.
   * - `true`: suppresses all advantage sources
   * - `false`/`undefined`: does nothing (suppression cannot be undone without reset)
   */
  set noAdvantage(value) {
    if (value === true) {
      this.#attackRollModifierTracker.advantage.suppress();
    }
  }
  /**
   * Backward compatibility getter for noDisadvantage.
   * Returns true if disadvantage is suppressed.
   */
  get noDisadvantage() {
    return this.#attackRollModifierTracker.disadvantage.isSuppressed;
  }
  /**
   * Backward compatibility setter for noDisadvantage.
   * - `true`: suppresses all disadvantage sources
   * - `false`/`undefined`: does nothing (suppression cannot be undone without reset)
   */
  set noDisadvantage(value) {
    if (value === true) {
      this.#attackRollModifierTracker.disadvantage.suppress();
    }
  }
  get dnd5eFlags() {
    return foundry.utils.getProperty(this.chatCard, "flags.dnd5e");
  }
  set dnd5eFlags(dnd5eFlags) {
    if (this.chatCard)
      addUpdatesCache(this.id, { "flags.dnd5e": dnd5eFlags });
  }
  get targetDescriptors() {
    return foundry.utils.getProperty(this.chatCard, "flags.dnd5e.targets" /* targetDescriptors */);
  }
  set targetDescriptors(targetDescriptors) {
    if (this.chatCard)
      addUpdatesCache(this.id, { ["flags.dnd5e.targets" /* targetDescriptors */]: targetDescriptors });
  }
  noAutoAttack;
  // override attack roll for standard care
  hitDisplayData;
  saveDisplayData;
  get utilityRoll() {
    if (!this.utilityRolls || this.utilityRolls.length === 0) return void 0;
    let finalRoll = this.utilityRolls.slice(1).reduce((rolls, roll) => addRollTo(rolls, roll), this.utilityRolls[0]);
    return finalRoll;
  }
  set utilityRoll(roll) {
    if (roll) this.setUtilityRoll(roll);
  }
  get damageRoll() {
    if (!this.damageRolls || this.damageRolls.length === 0) return void 0;
    let finalRoll = this.damageRolls.slice(1).reduce((rolls, roll) => addRollTo(rolls, roll), this.damageRolls[0]);
    return finalRoll;
  }
  set damageRoll(roll) {
    if (roll) this.setDamageRoll(roll);
  }
  set bonusDamageRoll(roll) {
    if (roll) this.setBonusDamageRoll(roll);
  }
  get bonusDamageRoll() {
    if (!this.bonusDamageRolls || this.bonusDamageRolls.length === 0) return void 0;
    let finalRoll = this.bonusDamageRolls.slice(1).reduce((rolls, roll) => addRollTo(rolls, roll), this.bonusDamageRolls[0]);
    return finalRoll;
  }
  get otherDamageRoll() {
    if (!this.otherDamageRolls || this.otherDamageRolls.length === 0) return void 0;
    let finalRoll = this.otherDamageRolls.slice(1).reduce((rolls, roll) => addRollTo(rolls, roll), this.otherDamageRolls[0]);
    return finalRoll;
  }
  set otherDamageRoll(roll) {
    if (roll) this.setOtherDamageRoll(roll);
  }
  get otherActivity() {
    return this.activity.otherActivity;
  }
  /**
   * Check if an activity has a valid save configuration (save with abilities, or check with ability/associated)
   */
  hasValidSaveOrCheck(activity) {
    if (!activity) return false;
    if (activity.save) {
      const abilities = activity.save.ability;
      if (abilities instanceof Set) return abilities.size > 0;
      return !!abilities;
    }
    if (activity.check) {
      return true;
    }
    return false;
  }
  get activityHasSave() {
    return this.hasValidSaveOrCheck(this.saveActivity);
  }
  get saveActivity() {
    if (this.hasValidSaveOrCheck(this.activity)) return this.activity;
    if (this.hasValidSaveOrCheck(this.otherActivity)) return this.otherActivity;
    return void 0;
  }
  get saveActivityDetails() {
    return this.saveActivity?.save || this.saveActivity?.check;
  }
  get saveItem() {
    if (this.activity.save || this.activity.check) return this.item;
    return this.saveActivity?.item ?? this.activity.item;
  }
  #rolls;
  get rolls() {
    if (this.#rolls) return this.#rolls;
    if (this.itemCardUuid && saveToChatCard) {
      this.#rolls = this.chatCard.rolls;
    }
    return this.#rolls ?? [];
  }
  get damageRolls() {
    return this.#persisted.rolls("damageRolls") ?? [];
  }
  set damageRolls(damageRolls) {
    this.#persisted.setRolls("damageRolls", damageRolls);
  }
  get bonusDamageRolls() {
    return this.#persisted.rolls("bonusDamageRolls");
  }
  set bonusDamageRolls(bonusDamageRolls) {
    this.#persisted.setRolls("bonusDamageRolls", bonusDamageRolls);
  }
  get otherDamageRolls() {
    return this.#persisted.rolls("otherDamageRolls");
  }
  set otherDamageRolls(otherDamageRolls) {
    this.#persisted.setRolls("otherDamageRolls", otherDamageRolls);
  }
  get utilityRolls() {
    return this.#persisted.rolls("utilityRolls") ?? [];
  }
  set utilityRolls(utilityRolls) {
    utilityRolls?.forEach((d) => d.data = {});
    this.#persisted.setRolls("utilityRolls", utilityRolls);
  }
  get damageTotal() {
    return this.#persisted.scalar("damageTotal", 0);
  }
  set damageTotal(damageTotal) {
    this.#persisted.setScalar("damageTotal", damageTotal);
  }
  get rawDamageDetail() {
    return this.#persisted.damageDetail("rawDamageDetail");
  }
  set rawDamageDetail(rawDamageDetail) {
    this.#persisted.setDamageDetail("rawDamageDetail", rawDamageDetail);
  }
  get rawOtherDamageDetail() {
    return this.#persisted.damageDetail("rawOtherDamageDetail");
  }
  set rawOtherDamageDetail(rawOtherDamageDetail) {
    this.#persisted.setDamageDetail("rawOtherDamageDetail", rawOtherDamageDetail);
  }
  get rawBonusDamageDetail() {
    return this.#persisted.damageDetail("rawBonusDamageDetail");
  }
  set rawBonusDamageDetail(rawBonusDamageDetail) {
    this.#persisted.setDamageDetail("rawBonusDamageDetail", rawBonusDamageDetail);
  }
  get damageDetail() {
    return this.#persisted.damageDetail("damageDetail");
  }
  set damageDetail(damageDetail) {
    this.#persisted.setDamageDetail("damageDetail", damageDetail);
  }
  get otherDamageDetail() {
    return this.#persisted.damageDetail("otherDamageDetail");
  }
  set otherDamageDetail(otherDamageDetail) {
    this.#persisted.setDamageDetail("otherDamageDetail", otherDamageDetail);
  }
  get bonusDamageDetail() {
    return this.#persisted.damageDetail("bonusDamageDetail");
  }
  set bonusDamageDetail(bonusDamageDetail) {
    this.#persisted.setDamageDetail("bonusDamageDetail", bonusDamageDetail);
  }
  damageRollHTML;
  get damageRollCount() {
    return this.#persisted.scalar("damageRollCount", 0);
  }
  set damageRollCount(damageRollCount) {
    this.#persisted.setScalar("damageRollCount", damageRollCount);
  }
  get defaultDamageType() {
    return this.#persisted.scalar("defaultDamageType", "");
  }
  set defaultDamageType(defaultDamageType) {
    this.#persisted.setScalar("defaultDamageType", defaultDamageType);
  }
  get effectsAlreadyExpired() {
    return this.#persisted.scalar("effectsAlreadyExpired", []);
  }
  set effectsAlreadyExpired(effectsAlreadyExpired) {
    this.#persisted.setScalar("effectsAlreadyExpired", effectsAlreadyExpired);
  }
  noAutoDamage;
  // override damage roll for damage rolls
  get saves() {
    return this.#persisted.tokenSet("saveUuids");
  }
  set saves(saves) {
    this.#persisted.setTokenSet("saveUuids", saves);
  }
  get superSavers() {
    return this.#persisted.tokenSet("superSaverUuids");
  }
  set superSavers(superSavers) {
    this.#persisted.setTokenSet("superSaverUuids", superSavers);
  }
  get semiSuperSavers() {
    return this.#persisted.tokenSet("semiSuperSaverUuids");
  }
  set semiSuperSavers(semiSuperSavers) {
    this.#persisted.setTokenSet("semiSuperSaverUuids", semiSuperSavers);
  }
  get failedSaves() {
    return this.#persisted.tokenSet("failedSaveUuids");
  }
  set failedSaves(failedSaves) {
    this.#persisted.setTokenSet("failedSaveUuids", failedSaves);
  }
  get fumbleSaves() {
    return this.#persisted.tokenSet("fumbleSaveUuids");
  }
  set fumbleSaves(fumbleSaves) {
    this.#persisted.setTokenSet("fumbleSaveUuids", fumbleSaves);
  }
  get criticalSaves() {
    return this.#persisted.tokenSet("criticalSaveUuids");
  }
  set criticalSaves(criticalSaves) {
    this.#persisted.setTokenSet("criticalSaveUuids", criticalSaves);
  }
  get advantageSaves() {
    return this.#persisted.tokenSet("advantageSaveUuids");
  }
  set advantageSaves(advantageSaves) {
    this.#persisted.setTokenSet("advantageSaveUuids", advantageSaves);
  }
  saveRequests;
  saveTimeouts;
  defenceRequests;
  defenceTimeouts;
  flagTags;
  #onUseMacros;
  get onUseMacros() {
    if (this.#onUseMacros) return this.#onUseMacros;
    if (configSettings.allowUseMacro) {
      this.#onUseMacros = new OnUseMacros();
      const itemOnUseMacros = this.item.getFlag(MODULE_ID, "onUseMacroParts") ?? new OnUseMacros();
      const actorOnUseMacros = this.actor.getFlag(MODULE_ID, "onUseMacroParts") ?? new OnUseMacros();
      if (this.workflowOptions?.onlyOnUseItemMacros) {
        this.#onUseMacros.items = [...itemOnUseMacros.items];
        this.onUseMacros = this.onUseMacros;
      } else {
        this.#onUseMacros.items = [...itemOnUseMacros.items, ...actorOnUseMacros.items];
        this.onUseMacros = this.onUseMacros;
      }
    }
    return this.#onUseMacros;
  }
  set onUseMacros(onUseMacros) {
    this.#onUseMacros = onUseMacros;
  }
  #ammunitionOnUseMacros;
  get ammunitionOnUseMacros() {
    if (this.#ammunitionOnUseMacros) return this.#ammunitionOnUseMacros;
    this.#ammunitionOnUseMacros = new OnUseMacros();
    const ammunitionOnUseMacros = this.ammunition?.getFlag(MODULE_ID, "onUseMacroParts") ?? new OnUseMacros();
    this.#ammunitionOnUseMacros.items = ammunitionOnUseMacros.items;
    return this.#ammunitionOnUseMacros;
  }
  set ammunitionOnUseMacros(ammunitionOnUseMacros) {
    this.#ammunitionOnUseMacros = ammunitionOnUseMacros;
    if (this.itemCardUuid && saveToChatCard) {
      addUpdatesCacheFlags(this.id, { ["ammunitionOnUseMacros" /* ammunitionOnUseMacros */]: ammunitionOnUseMacros }, "midi-qol");
    }
  }
  // Legacy Set - delegates to RollModifierTracker
  get attackAdvAttribution() {
    return this.#attackRollModifierTracker.legacyAttribution;
  }
  set attackAdvAttribution(_value) {
    this.#attackRollModifierTracker.reset();
  }
  // Structured attack attribution - delegates to attackRollModifierTracker
  // Uses AttributionMap: type -> source -> displayName for O(1) lookups
  get attackAttribution() {
    return this.#attackRollModifierTracker.attribution;
  }
  // Structured save attribution - keyed by target UUID
  // Uses AttributionMap per target for O(1) lookups
  #saveAttribution;
  get saveAttribution() {
    if (this.#saveAttribution) return this.#saveAttribution;
    if (this.itemCardUuid && saveToChatCard) {
      this.#saveAttribution = this.chatCard?.getFlag(MODULE_ID, "saveAttribution" /* saveAttribution */);
    }
    return this.#saveAttribution ?? {};
  }
  set saveAttribution(saveAttribution) {
    this.#saveAttribution = saveAttribution;
    if (this.itemCardUuid && saveToChatCard) {
      addUpdatesCacheFlags(this.id, { ["saveAttribution" /* saveAttribution */]: saveAttribution }, "midi-qol");
    }
  }
  /**
   * Add a structured attribution for a target's saving throw.
   * O(1) operation - uses object property assignment for deduplication.
   * @param targetUuid - The UUID of the target
   * @param type - Attribution type (ADV, DIS, etc.)
   * @param source - Source identifier
   * @param displayName - Human-readable display name
   */
  addSaveAttribution(targetUuid, type, source, displayName) {
    const current = this.#saveAttribution ?? {};
    current[targetUuid] ??= {};
    current[targetUuid][type] ??= {};
    current[targetUuid][type][source] = displayName;
    this.saveAttribution = current;
  }
  // Legacy Set for adv-reminder module - delegates to RollModifierTracker
  get advReminderAttackAdvAttribution() {
    return this.#attackRollModifierTracker.advReminderAttribution;
  }
  set advReminderAttackAdvAttribution(_value) {
    this.#attackRollModifierTracker.reset();
  }
  get noOptionalRules() {
    return this.#persisted.scalar("noOptionalRules", false);
  }
  set noOptionalRules(noOptionalRules) {
    this.#persisted.setScalar("noOptionalRules", noOptionalRules);
  }
  undoData;
  // Way too big to store. Undos are blanked on reload
  get bonusDamageFlavor() {
    return `(${this.bonusDamageRolls?.map((r) => r.options.flavor ?? r.options.type)})`;
  }
  get chatCard() {
    return getCachedDocument2(this.itemCardUuid);
  }
  get itemCardId() {
    return this.chatCard?.id;
  }
  get damageFlavor() {
    if (this.activity.type === "heal") return i18n("midi-qol.BaseHealingFlavor");
    return i18n("midi-qol.BaseDamageFlavor");
  }
  #randomItemId;
  get itemId() {
    if (this.item) return this.item.id;
    if (!this.#randomItemId) this.#randomItemId = foundry.utils.randomID();
    return this.#randomItemId;
  }
  get needTemplate() {
    return this.expectedTemplateCount > 0 && this.templateUuids.length < this.expectedTemplateCount;
  }
  get itemUuid() {
    return this.item.uuid;
  }
  get otherDamageFlavor() {
    if (this.otherActivity?.type === "heal") return i18n("midi-qol.OtherHealIngFlavor");
    if (this.otherActivity?.midiProperties?.displayActivityName) {
      return i18n(this.otherActivity.name);
    }
    return i18n("midi-qol.OtherDamageFlavor");
  }
  get useCondition() {
    return this.activity.useCondition;
  }
  get effectActivationCondition() {
    return this.activity.effectCondition;
  }
  get hasSave() {
    return Boolean(this.activity.save || this.activity.check || this.otherActivity?.save || this.otherActivity?.check);
  }
  get otherDamageFormula() {
    error2("workflow.otherDamageFormula is deprecated without replacement.");
    return "";
  }
  get otherUseCondition() {
    return this.otherActivity?.useCondition;
  }
  get otherEffectActivationCondition() {
    return this.otherActivity?.effectCondition;
  }
  get shouldRollDamage() {
    if (this.systemCard) return false;
    if (this.actor.type === configSettings.averageDamage || configSettings.averageDamage === "all") return true;
    const normalRoll = getAutoRollDamage(this) === "always" || !!(getAutoRollDamage(this) === "saveOnly" && this.activity.save && !this.activity.attack) || !!(getAutoRollDamage(this) !== "none" && (!this.activity.attack || configSettings.autoCheckHit === "none")) || !!(getAutoRollDamage(this) === "onHit" && (this.hitTargets.size > 0 || this.hitTargetsEC?.size > 0 || this.targets.size === 0)) || !!(getAutoRollDamage(this) === "onHit" && this.hitTargetsEC?.size > 0);
    return this.rollOptions?.rollToggle ? !normalRoll : normalRoll;
  }
  get spellLevel() {
    let spellLevel;
    if (this.chatCard) {
      spellLevel = isDnd5e53 ? foundry.utils.getProperty(this.chatCard, "system.spellLevel") : foundry.utils.getProperty(this.chatCard, "flags.dnd5e.use.spellLevel");
    }
    return spellLevel ?? this.castData?.castLevel ?? 0;
  }
  get workflowType() {
    return "BaseWorkflow";
  }
  setTargets(targets) {
    this.targets = new Set(targets);
    this.saves = /* @__PURE__ */ new Set();
    this.failedSaves = new Set(this.targets);
    this.hitTargets = /* @__PURE__ */ new Set();
    this.superSavers = /* @__PURE__ */ new Set();
    this.semiSuperSavers = /* @__PURE__ */ new Set();
    this.advantageSaves = /* @__PURE__ */ new Set();
    this.criticalSaves = /* @__PURE__ */ new Set();
    this.fumbleSaves = /* @__PURE__ */ new Set();
    this.hitTargetsEC = /* @__PURE__ */ new Set();
  }
  get saveRolls() {
    foundry.utils.logCompatibilityWarning("Workflow.saveRolls is deprecated without replacement. Use workflow.tokenSaves instead", { since: "midi-qol 13.0.42", until: "mid-qol 13.1.0" });
    if (this.tokenSaves)
      return Object.values(this.tokenSaves);
    return [];
  }
  ammunition;
  systemCard;
  aborted = false;
  workflowName;
  forceApplyEffects;
  onUseCalled;
  attackRolled;
  longRangeAttack;
  preSelectedTargets;
  postSummonHookId;
  summonedCreatures;
  transformedActorUuids;
  transformedActors;
  kickStart;
  disadvantageSaves;
  saveResults;
  /* D20Roll */
  stateTransitionCount;
  maxTransitionCount = 250;
  selfTargeted;
  workflowStartTime;
  rangeTargeting;
  tempTargetConfirmation;
  whisperAttackCard;
  effectTargets;
  // Deferred other activity damage rolling - stored when consumption needs to be checked first
  otherDamageConfig;
  otherDamageDialog;
  activationMatches;
  activationFails;
  otherActivationMatches;
  otherActivationFails;
  otherEffectTargets;
  allOtherEffectTargets;
  effectTargetsOnSave;
  otherEffectTargetsOnSave;
  otherDamageTotal;
  bonusDamageTotal;
  keepActivityCard;
  needsHitTargetsDisplayed = true;
  // TODO: Type this
  conditionData;
  flankingAdvantage;
  bonusDamageHTML;
  otherDamageList;
  macroPass;
  tag;
  activeDefenceDC;
  utilityRollHTML;
  useOther;
  otherDamageRollHTML;
  saveTargetsDisplayed;
  targetSaveDetails;
  // Per-target SaveDetails for attribution tracking
  /** Get save rolls keyed by target UUID - derived from targetSaveDetails */
  get tokenSaves() {
    if (!this.targetSaveDetails) return void 0;
    const result = {};
    for (const [uuid, details] of Object.entries(this.targetSaveDetails)) {
      if (details.saveRoll) result[uuid] = details.saveRoll;
    }
    return Object.keys(result).length > 0 ? result : void 0;
  }
  saveDC;
  saveDetails;
  damageItem;
  challengeModeScale;
  activeDefenceRolls;
  rollResults;
  healingAdjustedDamageTotal;
  flavor;
  healingAdjustedBonusDamageTotal;
  bonusDamageRollHTML;
  healingAdjustedOtherDamageTotal;
  attackMode;
  itemUsesReaction;
  itemUsesBonusAction;
  rangeDetails;
  sequenceId;
  targetACModifiers = /* @__PURE__ */ new Map();
  // Map of actor uuid to AC modifier, used for active defences
  // Does this do anything?
  applicationTargets;
  // TODO: these
  rollOptions;
  castData;
  throttleChatMessageUpdates;
  // TODO: type options
  constructor(actor, activity, speaker, targets, options = { chatCardUuid: void 0, storeWorkflow: true, item: void 0 }) {
    this.workflowStartTime = Date.now();
    if (options.chatCardUuid && saveToChatCard) {
      this.itemCardUuid = options.chatCardUuid;
      this.id = options.chatCardUuid;
    } else {
      if (!actor || !activity || !speaker) {
        error2(`Workflow was not provided with valid arguments: ${actor}, ${activity}, ${speaker}, ${targets}, ${options}`);
        throw new Error("Workflow could not be created");
      }
      this.activity = activity;
      this.item = activity.item ?? options.item;
      if (speaker.token) this.token = canvas.tokens?.get(speaker.token);
      if (options.token) this.token = options.token;
      if (!this.token) {
        this.token = getOrCreateTokenForActor(this.actor);
      }
      if (targets && (targets instanceof Set ? targets.size : targets.length) !== 0) this.targets = new Set(targets ? targets : []);
      if (speaker) this.speaker = speaker;
      this.hitTargets = /* @__PURE__ */ new Set();
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      this.initSaveResults();
      this.attackTotal = minAttackTotal;
      this.currentAction = this.WorkflowState_NoAction;
      this.suspended = true;
      this.aborted = false;
      if (!this.activity || this instanceof DummyWorkflow) {
        this.id = foundry.utils.randomID();
        this.workflowName = `workflow ${this.id}`;
      } else {
        this.id = this.activity.uuid;
        const workflowName = options.workflowOptions?.workflowName ?? this.item.name ?? "no item";
        this.workflowName = `${this.constructor.name} ${workflowName} ${foundry.utils.randomID()}`;
        if (options.storeWorkflow) _Workflow.addWorkflow(this);
      }
      this.noOptionalRules = options?.noOptionalRules ?? !configSettings.optionalRulesEnabled;
      this.attackRollCount = 0;
      this.damageRollCount = 0;
      this.advantage = void 0;
      this.disadvantage = void 0;
      this.targetsCanSense = /* @__PURE__ */ new Set();
      this.targetsCanSee = /* @__PURE__ */ new Set();
      this.tokenCanSense = /* @__PURE__ */ new Set();
      this.tokenCanSee = /* @__PURE__ */ new Set();
      this.rawDamageDetail = [];
      this.rawOtherDamageDetail = [];
      this.effectsAlreadyExpired = [];
      this.workflowOptions = options?.workflowOptions ?? {};
      if (this.speaker.scene) this.speaker.scene = canvas.scene?.id ?? "";
    }
    this.saveRequests = {};
    this.defenceRequests = {};
    this.saveTimeouts = {};
    this.defenceTimeouts = {};
    this.forceApplyEffects = false;
    this.onUseCalled = false;
    this.attackRolled = false;
    this.flagTags = void 0;
    if (options.rollOptions) this.rollOptions = options.rollOptions;
    else this.rollOptions = {};
    if (options.event) this.rollOptions.event = options.event;
    else if (options.workflowOptions?.event) this.rollOptions.event = options.workflowOptions.event;
    this.workflowOptions = foundry.utils.mergeObject(defaultRollOptions, {
      autoRollAttack: getAutoRollAttack(this),
      autoRollDamage: getAutoRollDamage(this),
      fastForwardAttack: isAutoFastForward2("attack", { workflow: this }),
      fastForwardDamage: isAutoFastForward2("damage", { workflow: this })
    }, { inplace: false });
    if (options.workflowOptions)
      foundry.utils.mergeObject(this.workflowOptions, options.workflowOptions, { overwrite: true });
    if (this.activity.midiProperties?.noConcentrationCheck) this.workflowOptions.noConcentrationCheck = true;
    if (this.workflowOptions.templateUuid) this.templateUuid = this.workflowOptions.templateUuid;
    this.defaultDamageType = getActivityDefaultDamageType(this) ?? MidiQOL.MQdefaultDamageType;
    MidiQOL.MQdefaultDamageType = this.defaultDamageType;
    if (this.activity.actionType === "heal" && !Object.keys(GameSystemConfig.healingTypes).includes(this.defaultDamageType ?? "")) this.defaultDamageType = "healing";
    if (this.workflowOptions?.preSelectedTargetUuids?.length) {
      this.preSelectedTargets = new Set(
        this.workflowOptions.preSelectedTargetUuids.map((uuid) => fromUuidSync(uuid)?.object).filter((t) => !!t)
      );
    } else {
      this.preSelectedTargets = canvas?.scene ? new Set(game.user?.targets ?? []) : /* @__PURE__ */ new Set();
    }
    if (activityHasAreaTarget(this.activity) && !activityHasAutoPlaceTemplate(this.activity)) {
      this.expectedTemplateCount = Number(this.activity?.target?.template?.count) || 1;
    } else {
      this.expectedTemplateCount = 0;
    }
    this.templateUuids = [];
    if (this.activity instanceof MidiSummonActivity && configSettings.autoRemoveSummonedCreature) {
      this.postSummonHookId = Hooks.on("dnd5e.postSummon", (activity2, profile, createdTokens, options2) => {
        if (activity2?.uuid !== this.activity?.uuid) return;
        this.summonedCreatures = createdTokens;
      });
    }
    this.itemUseComplete = false;
    this.kickStart = false;
    this.throttleChatMessageUpdates = getThrottlingFunction();
  }
  someEventKeySet() {
    const event = this.rollOptions.event;
    return event?.shiftKey || event?.altKey || event?.ctrlKey || event?.metaKey;
  }
  someAutoRollEventKeySet() {
    const event = this.rollOptions.event;
    return event?.altKey || event?.ctrlKey || event?.metaKey;
  }
  static async removeItemCardConfirmRollButton(itemCardUuid) {
    const chatMessage = getCachedDocument2(itemCardUuid);
    const workflow = _Workflow.getWorkflow(itemCardUuid);
    if (!chatMessage?.content) return;
    let content = chatMessage.content;
    const confirmMissRe = /<button class="midi-qol-confirm-damage-roll-complete-miss" data-action="confirmDamageRollCompleteMiss">[^<]*?<\/button>/;
    content = content?.replace(confirmMissRe, "");
    const confirmRe = /<button class="midi-qol-confirm-damage-roll-complete" data-action="confirmDamageRollComplete">[^<]*?<\/button>/;
    content = content?.replace(confirmRe, "");
    const confirmHitRe = /<button class="midi-qol-confirm-damage-roll-complete-hit" data-action="confirmDamageRollCompleteHit">[^<]*?<\/button>/;
    content = content?.replace(confirmHitRe, "");
    const cancelRe = /<button class="midi-qol-confirm-damage-roll-cancel" data-action="confirmDamageRollCancel">[^<]*?<\/button>/;
    content = content?.replace(cancelRe, "");
    return await throttledUpdate(chatMessage, { content }, workflow?.throttleChatMessageUpdates, false);
  }
  static async removeItemCardButtons(itemCardUuid, { removeAllButtons = false, removeAttackButtons = true, removeDamageButtons = false, removeDnD5eButtons = false, removeConfirmButtons = false, removeAllDnd5eButtons = false, removeDnd5eSaveButtons = false, removeDnd5eTemplateButtons = false } = {}) {
    try {
      const workflow = _Workflow.getWorkflow(itemCardUuid);
      const chatMessage = workflow?.chatCard;
      if (!chatMessage?.content) return;
      let content = chatMessage?.content;
      let allowRemoveAll = !(workflow && workflow.activity?.target.affects.type === "" && workflow.targets.size === 0);
      if (removeAllButtons && allowRemoveAll) {
        const buttonRe = /<button[\s\S]*?<\/button>/gi;
        content = content.replace(buttonRe, "");
        return await throttledUpdate(chatMessage, { content }, workflow?.throttleChatMessageUpdates, false);
      } else {
        if (removeAttackButtons) {
          const attackRe = /<div class="midi-qol-attack-buttons[^"]*">[\s\S]*?<\/div>/;
          content = content?.replace(attackRe, "");
        }
        if (removeDamageButtons) {
          const damageRe = /<div class="midi-qol-damage-buttons[^"]*">[\s\S]*?<\/div>/;
          const otherDamageRe = /<button class="midi-qol-otherDamage-button" data-action="rollDamage">[^<]*<\/button>/;
          const formulaRe = /<button data-action="rollFormula">[^<]*<\/button>/;
          content = content?.replace(damageRe, "");
          content = content?.replace(otherDamageRe, "");
          content = content?.replace(formulaRe, "");
        }
        if (removeAllDnd5eButtons) {
          const dnd5eRe = /<div class="midi-dnd5e-buttons">[\s\S]*?<div class="end-midi-dnd5e-buttons">/;
          content = content?.replace(dnd5eRe, "<div class='midi-dnd5e-buttons'></div><div class='end-midi-dnd5e-buttons'>");
        } else {
          if (removeDnd5eSaveButtons) {
            const saveRe = /<button[\s\S].*?data-action="rollSave"[\s\S]*?<\/button>/;
            for (let count = 0; count < 100; count++) {
              if (!content.match(saveRe)) break;
              content = content?.replace(saveRe, "");
            }
          }
          if (removeDnd5eTemplateButtons) {
            const templateRe = /<button[\s\S].*?data-action="placeTemplate"[\s\S]*?<\/button>/;
            content = content?.replace(templateRe, "");
          }
        }
        if (removeConfirmButtons) {
          const confirmMissRe = /<button class="midi-qol-confirm-damage-roll-complete-miss" data-action="confirmDamageRollCompleteMiss">[^<]*?<\/button>/;
          content = content?.replace(confirmMissRe, "");
          const confirmRe = /<button class="midi-qol-confirm-damage-roll-complete" data-action="confirmDamageRollComplete">[^<]*?<\/button>/;
          content = content?.replace(confirmRe, "");
          const confirmHitRe = /<button class="midi-qol-confirm-damage-roll-complete-hit" data-action="confirmDamageRollCompleteHit">[^<]*?<\/button>/;
          content = content?.replace(confirmHitRe, "");
          const cancelRe = /<button class="midi-qol-confirm-damage-roll-cancel" data-action="confirmDamageRollCancel">[^<]*?<\/button>/;
          content = content?.replace(cancelRe, "");
        }
        return await throttledUpdate(chatMessage, { content }, workflow?.throttleChatMessageUpdates);
      }
    } catch (err) {
      const message = `removeItemCardButtons`;
      TroubleShooter.recordError(err, message);
      throw err;
    }
  }
  static async removeWorkflow(id) {
    if (!id) {
      warn("removeWorkflow | No id");
      return;
    }
    const workflow = _Workflow.getWorkflow(id);
    if (!workflow) {
      if (debugEnabled > 0) warn("removeWorkflow | No such workflow ", id);
      return;
    }
    if (workflow.postSummonHookId) Hooks.off("dnd5e.postSummon", workflow.postSummonHookId);
    if (debugEnabled > 0) warn(`removeWorkflow deleting ${id}`, _Workflow.workflows.get(id));
    _Workflow.workflows.delete(id);
    if (workflow.itemCardUuid) {
      if (workflow.currentAction === workflow.WorkflowState_ConfirmRoll) {
        const itemCard = workflow.chatCard;
        if (itemCard) await itemCard.delete();
        clearUpdatesCache(workflow.itemCardUuid);
      } else {
        await _Workflow.removeItemCardButtons(workflow.itemCardUuid, { removeAllButtons: true });
        setTimeout(() => {
          const chatMessageElt = document?.querySelector(`[data-message-id="${workflow.itemCardId ?? "XXX"}"]`);
          if (chatMessageElt) chatMessageElt.querySelectorAll(".collapsible").forEach((ce) => {
            if (!ce.classList.contains("collapsed")) ce.classList.add("collapsed");
          });
        }, 1);
      }
    }
  }
  static get stateTable() {
    const table = {};
    Reflect.ownKeys(this.prototype).filter((k) => k.toString().startsWith("WorkflowState_")).forEach((k) => table[k.toString()] = this.prototype[k.toString()]);
    return table;
  }
  static get stateHooks() {
    const hooks = {};
    for (let state of Object.values(this.stateTable)) {
      const name = this.nameForState(state);
      hooks[`pre${name}`] = `before ${name} (S*)`;
      hooks[`post${name}`] = `after ${name} (S*)`;
    }
    return hooks;
  }
  static get allHooks() {
    const allHooks = foundry.utils.mergeObject(onUseMacroOptions, this.stateHooks, { inplace: false });
    return allHooks;
  }
  static get allMacroPasses() {
    return this.allHooks;
  }
  async callHooksForAction(prePost, action) {
    if (!action) {
      console.warn("midi-qol | callPreHooksForAction | No action");
      return true;
    }
    if (debugEnabled > 1) log(`callHooksForAction | ${prePost} ${this.nameForState(action)}`);
    return this.callCancellableHooks(`midi-qol.${prePost}${this.nameForState(action)}`);
  }
  async callOnUseMacrosForAction(prePost, action) {
    if (!configSettings.allowUseMacro || this.workflowOptions.noOnUseMacro === true) {
      warn(`Calling ${prePost}${this.nameForState(action)} disabled due to macro call settings`);
      return [];
    }
    if (!action) {
      console.warn("midi-qol | callOnUseMacrosForAction | No action");
      return [];
    }
    if (debugEnabled > 1) log(`callOnUseMacrosForAction | ${prePost} ${this.nameForState(action)}`);
    const macroPass = `${prePost}${this.nameForState(action)}`;
    if (this.ammunition) await this.callMacros(this.item, this.ammunitionOnUseMacros?.getMacros(macroPass), "OnUse", macroPass);
    if (debugEnabled > 1) log(`callOnUseMacrosForAction | ${this.onUseMacros?.getMacros(macroPass)}`);
    return this.callMacros(this.item, this.onUseMacros?.getMacros(macroPass), "OnUse", macroPass);
  }
  static nameForState(state) {
    if (state === void 0) return "undefined";
    return state.name.replace(/^WorkflowState_/, "") ?? state.name;
  }
  nameForState(state) {
    return _Workflow.nameForState(state);
  }
  /**
   *
   * @param context context to be passed to the state call. Typically the data that caused the an unsuspend to fire, but can be others
   * Trigger execution of the current state with the context that triggered the unsuspend. e.g. attackRoll or damageRoll
   */
  async unSuspend(context = {}) {
    if (context.itemCardUuid) {
      this.itemCardUuid = context.itemCardUuid;
    }
    if (context.itemUseComplete) this.itemUseComplete = true;
    if (this.suspended) {
      this.suspended = false;
      this.performState(this.currentAction, context);
    }
  }
  /**
   * Process templates that have been placed, integrating them with the workflow.
   * Validates template count, deletes incomplete placements, sets up template tracking,
   * cleanup, targeting, and advances the workflow if suspended.
   * @param templates - Array of placed template documents
   * @param activity - The activity that placed the templates (used to determine expected count)
   * @returns true if all expected templates were placed and processed, false if incomplete
   */
  async processPlacedTemplates(templates, activity) {
    if (!templates || templates.length === 0) return false;
    const expectedCount = Number(activity?.target?.template?.count) || 1;
    if (templates.length < expectedCount) {
      for (const tDoc of templates) {
        try {
          await tDoc.delete();
        } catch (e) {
        }
      }
      return false;
    }
    for (const tDoc of templates) {
      this.templateUuid = tDoc.uuid;
      await this.setupTemplateCleanup(tDoc);
    }
    this.placedTemplateCount = templates.length;
    this.selectAllTemplateTargets(activity);
    if (this.suspended && this.currentAction === this.WorkflowState_AwaitTemplate) {
      this.unSuspend();
    }
    return true;
  }
  /**
   * Compute targets from all placed templates and update targeting state.
   * Called once after ALL templates are placed, rather than per-template.
   */
  selectAllTemplateTargets(activity) {
    if (!activity) activity = this.activity;
    const selfToken = getToken(activity?.actor);
    const ignoreSelf = (activity?.target?.affects.special ?? "").split(";").some((spec) => ["-self"].includes(spec));
    const AoETargetType = activity ? getAoETargetType2(activity) : "any";
    const targeting = getActivityAutoTargetAction2(activity);
    if (targeting === "none") return;
    let allTargetTokens = computeTargetsFromTemplates(this.templateUuids, selfToken, ignoreSelf, AoETargetType, targeting);
    if (activity?.target?.affects.count) {
      const maxTargets = Number(activity.target.affects.count) || 0;
      if (maxTargets > 0 && allTargetTokens.length > maxTargets) {
        allTargetTokens = allTargetTokens.slice(0, maxTargets);
      }
    }
    const allIds = allTargetTokens.map((t) => t.id).filter(Boolean);
    updateUserTargets(allIds);
    this.setTargets(new Set(game.user?.targets ?? []));
  }
  /**
   * Sets up the deletion mechanism for a template as early as possible.
   * For concentration activities, adds template as dependent of concentration effect.
   * For non-concentration activities with duration, creates a template deletion effect.
   *
   * @param templateDocument The template document to set up cleanup for
   * @returns true if a cleanup mechanism was set up, false otherwise
   */
  async setupTemplateCleanup(templateDocument) {
    if (!templateDocument) return false;
    if (configSettings.autoRemoveInstantaneousTemplate && this.activity?.duration?.units === "inst") {
      return false;
    }
    const concentrationEffect = getConcentrationEffect(this.actor, this.item);
    if (concentrationEffect) {
      await templateDocument.setFlag("dnd5e", "dependentOn", concentrationEffect.uuid);
      return true;
    }
    if (!installedModules.get("dae") || !configSettings.autoRemoveTemplate) {
      return false;
    }
    const templateString = " " + i18n("midi-qol.MeasuredTemplate");
    const existingEffect = this.actor?.effects.find(
      (ef) => ef.name === this.item.name + templateString
    );
    if (existingEffect) {
      await templateDocument.setFlag("dnd5e", "dependentOn", existingEffect.uuid);
      return true;
    }
    const activityDuration = this.activity.duration;
    if (!activityDuration?.units) return false;
    const selfTarget = this.token;
    const inCombat = game.combat?.turns.some(
      (combatant) => combatant.tokenId === selfTarget?.id
    );
    const converted = convertDuration(activityDuration, inCombat);
    if (converted.type === "none") {
      return false;
    }
    const effectData = {
      origin: this.item.uuid,
      disabled: false,
      img: this.item.img,
      name: this.item.name + templateString,
      duration: {},
      flags: {
        dae: { stackable: "noneName" }
      }
    };
    if (converted.type === "seconds") {
      effectData.duration = { startTime: game.time?.worldTime };
      if (converted.seconds !== void 0) {
        effectData.duration.seconds = converted.seconds;
      }
    } else if (converted.type === "turns") {
      effectData.duration = {
        startRound: game.combat?.round,
        startTurn: game.combat?.turn
      };
      if (converted.rounds !== void 0) {
        effectData.duration.rounds = converted.rounds;
      }
      if (converted.turns !== void 0) {
        effectData.duration.turns = converted.turns;
      }
    }
    const effects = await this.actor.createEmbeddedDocuments("ActiveEffect", [effectData]);
    if (effects.length > 0) {
      await templateDocument.setFlag("dnd5e", "dependentOn", effects[0].uuid);
      return true;
    }
    return false;
  }
  /**
   * Render the attribution tooltip HTML for display.
   * Converts the attackAttribution map to an array and renders the attribution-tooltip template.
   * @returns The rendered HTML string
   */
  async renderAttributionTooltip() {
    const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
    const mode = this.attackRoll?.options?.advantageMode ?? this.attackRollModifierTracker.advantageMode ?? ADV_MODE.NORMAL;
    const title = getAdvantageModeTitle(mode);
    return renderAttributionTooltip(this.attackAttribution ?? {}, title);
  }
  /**
   *
   * @param newState the state to execute
   * @param context context to be passed to the state call. Typically the data that caused the an unsuspend to fire, but can be others
   * Continues to execute states until suspended, aborted or the state transition count is exceeded.
   */
  async performState(newState, context = {}) {
    if (this.stateTransitionCount === void 0) this.stateTransitionCount = 0;
    let isAborting = this.aborted;
    try {
      while (this.stateTransitionCount < this.maxTransitionCount) {
        const currentName = this.nameForState(this.currentAction);
        this.suspended = false;
        this.stateTransitionCount += 1;
        isAborting ||= this.aborted || newState === this.WorkflowState_Abort;
        if (newState === void 0) {
          const message = `${this.workflowName} Perform state called with undefined action - previous state was ${this.nameForState(this.currentAction)}`;
          error2(message);
          TroubleShooter.recordError(new Error(message), message);
          this.suspended = true;
          break;
        }
        const name = this.nameForState(newState);
        await busyWait(1);
        if (this.currentAction !== newState) {
          if (!isAborting || this.currentAction === this.WorkflowState_Completed) {
            await this.callOnUseMacrosForAction("post", this.currentAction);
            if (await this.callHooksForAction("post", this.currentAction) === false && !isAborting) {
              console.warn(`${this.workflowName}${this.id} ${currentName} -> ${name} aborted by post ${this.nameForState(this.currentAction)} Hook`);
              newState = this.aborted ? this.WorkflowState_Abort : this.WorkflowState_RollFinished;
            }
            if (debugEnabled > 0) warn(`${this.workflowName} ${this.id} finished ${currentName}`);
            if (debugEnabled > 0) warn(`${this.workflowName} ${this.id} transition ${this.nameForState(this.currentAction)} -> ${name}`);
            if (!isAborting && this.aborted) {
              console.warn(`${this.workflowName} ${this.id} ${currentName} -> ${name} aborted by pre ${this.nameForState(this.currentAction)} macro pass`);
              newState = this.WorkflowState_Abort;
              continue;
            }
          }
          await this.callOnUseMacrosForAction("pre", newState);
          if (await this.callHooksForAction("pre", newState) === false && !isAborting) {
            console.warn(`${this.workflowName} ${this.id} ${currentName} -> ${name} aborted by pre ${this.nameForState(newState)} Hook`);
            newState = this.aborted ? this.WorkflowState_Abort : this.WorkflowState_RollFinished;
            continue;
          }
          if (this.aborted && !isAborting) {
            console.warn(`${this.workflowName} ${currentName} ${this.id} -> ${name} aborted by pre ${this.nameForState(newState)} macro pass`);
            newState = this.WorkflowState_Abort;
            continue;
          }
          this.currentAction = newState;
        }
        let nextState = await this.currentAction.bind(this)(context);
        if (nextState === this.WorkflowState_Suspend) {
          this.suspended = true;
          this.performThrottledUpdate(this.chatCard, {}, false);
          if (debugEnabled > 0) warn(`${this.workflowName} ${this.nameForState(this.currentAction)} -> suspended Workflow ${this.id}`);
          break;
        }
        newState = nextState;
        context = {};
      }
      if (this.stateTransitionCount >= this.maxTransitionCount) {
        const message = `performState | ${this.workflowName} Workflow ${this.id} exceeded ${this.maxTransitionCount} iterations`;
        error2(message);
        TroubleShooter.recordError(new Error(message), message);
      }
    } catch (err) {
      const message = `performState | ${this.workflowName} Workflow ${this.id}`;
      error2(message, err);
      TroubleShooter.recordError(err, message);
    }
  }
  async WorkflowState_Suspend(context = {}) {
    const message = `${this.workflowName} Workflow ${this.id} suspend should never be called`;
    error2(message);
    TroubleShooter.recordError(new Error(message), message);
    return void 0;
  }
  async WorkflowState_NoAction(context = {}) {
    if (debugEnabled > 0) warn("WorkflowState_NoAction", context);
    if (context.itemUseComplete) return this.WorkflowState_Start;
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_Start(context = {}) {
    if (!this.selfTargeted) {
      clearTargetSave();
    }
    this.rangeTargeting = activityHasEmanationNoTemplate(this.activity);
    if (this.rangeTargeting) {
      return this.WorkflowState_AoETargetConfirmation;
    }
    this.tempTargetConfirmation = getActivityAutoTargetAction2(this.activity) !== "none" && activityHasAreaTarget(this.activity);
    if (debugEnabled > 1) debug("WORKFLOW NONE", getActivityAutoTargetAction2(this.activity), activityHasAreaTarget(this.activity));
    if (this.tempTargetConfirmation) {
      return this.WorkflowState_AwaitTemplate;
    }
    return this.WorkflowState_AoETargetConfirmation;
  }
  async WorkflowState_AwaitItemCard(context = {}) {
    if (this.needItemCard || !this.itemUseComplete) {
      if (debugEnabled > 0) warn("WorkflowState_AwaitItemCard suspending because needItemCard/itemUseComplete", this.needItemCard, this.itemUseComplete);
      return this.WorkflowState_Suspend;
    }
    if (this.needTemplate) {
      if (debugEnabled > 0) warn("WorkflowState_AwaitItemCard  needTemplate -> await template");
      return this.WorkflowState_AwaitTemplate;
    }
    if (debugEnabled > 0) warn("WorkflowState_AwaitItemCard  -> TemplatePlaced");
    return this.WorkflowState_TemplatePlaced;
  }
  async WorkflowState_AwaitTemplate(context = {}) {
    if (debugEnabled > 0) warn("WorkflowState_AwaitTemplate started");
    if (this.templateUuids.length >= this.expectedTemplateCount) {
      if (debugEnabled > 0) warn("WorkflowState_AwaitTemplate context - template placed", "needTemplate", this.needTemplate, "needItemCard", this.needItemCard, "itemUseComplete", this.itemUseComplete);
      return this.WorkflowState_TemplatePlaced;
    }
    if (context.itemUseComplete || !this.needTemplate) {
      if (debugEnabled > 0) warn("WorkflowState_AwaitTemplate context itemUseComplete", "needTemplate", this.needTemplate, "needItemCard", this.needItemCard, "itemUseComplete", this.itemUseComplete);
      return this.WorkflowState_AoETargetConfirmation;
    }
    if (debugEnabled > 0) warn("WorkflowState_AwaitTemplate suspending", "needTemplate", this.needTemplate, "needItemCard", this.needItemCard, "itemUseComplete", this.itemUseComplete);
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_TemplatePlaced(context = {}) {
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("templatePlaced"), "OnUse", "templatePlaced");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("templatePlaced"), "OnUse", "templatePlaced");
    }
    if (!this.itemCardUuid) return this.WorkflowState_AoETargetConfirmation;
    const chatMessage = this.chatCard;
    if (!chatMessage?.content) return this.WorkflowState_AoETargetConfirmation;
    this.targets = validTargetTokens(this.targets);
    this.hitTargets = /* @__PURE__ */ new Set();
    this.hitTargetsEC = /* @__PURE__ */ new Set();
    let content = chatMessage.content;
    if (this.placedTemplateCount >= (Number(this.activity?.target?.template?.count) || 1)) {
      const buttonRe = /<button.*?data-action="placeTemplate"[^>]*?>[\s\S]*?<\/button>/;
      content = chatMessage.content?.replace(buttonRe, "");
    }
    await this.performThrottledUpdate(chatMessage, { content, flags: { "midi-qol": { type: MESSAGE_TYPES.ITEM } }, style: CONST.CHAT_MESSAGE_STYLES.OTHER });
    return this.WorkflowState_AoETargetConfirmation;
  }
  async WorkflowState_AoETargetConfirmation(context = {}) {
    if (!await postTemplateConfirmTargets(this.activity, { workflowOptions: this.workflowOptions }, this)) {
      return this.WorkflowState_Abort;
    }
    const hasScaledTargetCount = activityHasScaledTargetCount(this.activity);
    if (hasScaledTargetCount) {
      const allowedTargets = Number(this.activity.target?.affects?.count);
      if (allowedTargets && this.targets.size > allowedTargets) {
        ui.notifications?.warn(i18nFormat2("midi-qol.wrongNumberTargets", { allowedTargets }));
        return this.WorkflowState_Abort;
      }
    }
    return this.WorkflowState_ValidateRoll;
  }
  async WorkflowState_ValidateRoll(context = {}) {
    if (configSettings.allowUseMacro && this.workflowOptions.noTargetOnUseMacro !== true) {
      await this.triggerTargetMacros(["isTargeted"]);
      if (this.aborted) return this.WorkflowState_Abort;
    }
    if (checkMechanic("checkRange") !== "none" && (!this.AoO && ["rwak", "rsak"].includes(this.activity.actionType ?? "")) && this.tokenUuid) {
      const { result, attackingToken, range, longRange } = checkActivityRange(this.activity, getToken(this.token) ?? "invalid", this.targets);
      switch (result) {
        case "fail":
          return this.WorkflowState_RollFinished;
        case "dis":
          this.longRangeAttack = true;
      }
      if (attackingToken && this.token !== attackingToken) {
        this.token = attackingToken;
        await this.activity.setupCanSeeSense({ workflow: this });
      }
    }
    if (!this.workflowOptions.allowIncapacitated && checkMechanic("incapacitated") !== "nothing" && checkIncapacitated(this.actor, debugEnabled > 0, true)) {
      if (checkMechanic("incapacitated") === "enforce") return this.WorkflowState_RollFinished;
    }
    return this.WorkflowState_PreambleComplete;
  }
  async WorkflowState_PreambleComplete(context = {}) {
    if (configSettings.undoWorkflow) await saveTargetsUndoData(this);
    this.effectsAlreadyExpired = [];
    if (await this.callCancellableHooks("midi-qol.targetingComplete") === false) {
      ui.notifications?.warn(`midi-qol | Targeting complete hook cancelled roll`);
      return this.WorkflowState_Abort;
    }
    if (!getAutoRollAttack(this) && this.activity.attack || this.activity instanceof MidiTransformActivity) {
      const rollMode = safeGetGameSetting("core", "rollMode");
      this.whisperAttackCard = configSettings.autoCheckHit === "whisper" || rollMode === CONST.DICE_ROLL_MODES.BLIND || rollMode === CONST.DICE_ROLL_MODES.PRIVATE;
      if (this.activity.target?.affects?.type !== "self") {
        await this.displayHitTargets(this.whisperAttackCard);
      }
    }
    return this.WorkflowState_WaitForAttackRoll;
  }
  get postAttackRollState() {
    return this.WorkflowState_WaitForDamageRoll;
  }
  async WorkflowState_WaitForAttackRoll(context = {}) {
    if (context.attackRoll) {
      if (this.abort) return this.WorkflowState_Abort;
      return this.WorkflowState_AttackRollComplete;
    }
    if (!this.activity.attack) {
      this.hitTargets = new Set(this.targets);
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      if (this.hasSave) {
        this.needsHitTargetsDisplayed = false;
      } else if (this.targets.size > 0 && this.activity.target?.affects?.type !== "self") {
        await this.displayHitTargets(false);
      }
      if (this.activity.roll?.formula) return this.WorkflowState_WaitForUtilityRoll;
      return this.postAttackRollState;
    }
    const isFastRoll = this.rollOptions.fastForwardAttack ?? this.workflowOptions.fastForwardAttack ?? isAutoFastForward2("attack", { workflow: this });
    this.rollOptions.fastForwardAttack ||= isFastRoll;
    const autoRollAttack = getAutoRollAttack(this);
    if (!autoRollAttack) {
      const chatMessage = this.chatCard;
      let content = chatMessage?.content;
      if (content && !isFastRoll) {
        const searchRe = /(<button[^>]*data-action="rollAttack"[^>]*>)<span>[^<]*<\/span>(<\/button>)/;
        const hasAdvantage = this.advantage && !this.disadvantage;
        const hasDisadvantage = this.disadvantage && !this.advantage;
        let attackString = hasAdvantage ? i18n("DND5E.Advantage") : hasDisadvantage ? i18n("DND5E.Disadvantage") : i18n("DND5E.Attack");
        let replaceString = `$1<span>${attackString}</span>$2`;
        content = content.replace(searchRe, replaceString);
        await this.performThrottledUpdate(chatMessage, { content });
      } else if (!chatMessage) {
        const message = `WaitForAttackRoll | no chat message`;
        error2(message);
        TroubleShooter.recordError(new Error(message), message);
      }
    }
    if (this.activity.roll?.formula) return this.WorkflowState_WaitForUtilityRoll;
    if (autoRollAttack) {
      this.rollOptions.fastForwardAttack ||= isFastRoll;
      this.activity.rollAttack({ event: this.rollOptions.event, workflow: this, midiOptions: { ...this.rollOptions, workflowOptions: this.workflowOptions } }, {}, {});
    }
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_AttackRollComplete(context = {}) {
    if (configSettings.allowUseMacro && this.workflowOptions.noTargetOnUseMacro !== true) await this.triggerTargetMacros(["isAttacked"]);
    await this.processAttackRoll();
    await this.callMidiHooks("midi-qol.preCheckHits");
    if (this.aborted) return this.WorkflowState_Abort;
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("preCheckHits"), "OnUse", "preCheckHits");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("preCheckHits"), "OnUse", "preCheckHits");
    }
    await this.processAttackRoll();
    this.recordAttackStats();
    if (configSettings.autoCheckHit !== "none") {
      await this.displayAttackRoll({ GMOnlyAttackRoll: true });
      this.recordTargetACModifiers();
      await this.recordFlankingBonuses();
      await this.checkHits();
      await this.displayAttackRoll();
      const rollMode = safeGetGameSetting("core", "rollMode");
      this.whisperAttackCard = configSettings.autoCheckHit === "whisper" || rollMode === CONST.DICE_ROLL_MODES.BLIND || rollMode === CONST.DICE_ROLL_MODES.PRIVATE;
      await this.callMidiHooks("midi-qol.hitsChecked");
      if (this.aborted) return this.WorkflowState_Abort;
      await this.displayHits(this.whisperAttackCard);
    } else {
      await this.displayAttackRoll();
    }
    if (checkRule("removeHiddenInvis")) await removeHidden.bind(this)();
    if (checkRule("removeHiddenInvis")) await removeInvisible.bind(this)();
    await this.expireTargetEffects(["isAttacked"]);
    await this.callMidiHooks("midi-qol.AttackRollComplete");
    if (this.aborted) return this.WorkflowState_Abort;
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("postAttackRoll"), "OnUse", "postAttackRoll");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("postAttackRoll"), "OnUse", "postAttackRoll");
      if (this.aborted) return this.WorkflowState_Abort;
    }
    if (configSettings.autoCheckHit === "none") {
      this.hitTargets = /* @__PURE__ */ new Set();
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      if (this.targets.size > 0 && this.activity.target?.affects?.type !== "self") {
        await this.displayHitTargets(false);
      }
      return this.postAttackRollState;
    }
    if (getAutoRollDamage(this) === "always") return this.postAttackRollState;
    const noHits = this.hitTargets.size === 0 && this.hitTargetsEC.size === 0;
    const allMissed = noHits && this.targets.size !== 0;
    if (allMissed) {
      if (configSettings.confirmAttackDamage !== "none" && !this.workflowOptions.forceCompletion)
        return this.WorkflowState_ConfirmRoll;
      if (this.workflowOptions.forceCompletion || configSettings.autoCompleteWorkflow) {
        expireMyEffects.bind(this)(["1Attack", "1Action", "1Spell"]);
        return this.WorkflowState_RollFinished;
      }
    }
    return this.postAttackRollState;
  }
  _formatAttackTargets() {
    const targets = /* @__PURE__ */ new Map();
    for (const token of this.targets) {
      const { name } = token;
      const { img, system, uuid } = token.actor ?? {};
      const ac = system?.attributes?.ac?.value ?? "";
      if (uuid && Number.isNumeric(ac)) targets.set(uuid, { name, img, uuid, ac });
    }
    return Array.from(targets.values());
  }
  async WorkflowState_WaitForDamageRoll(context = {}) {
    if (context.damageRoll || context.otherDamageRoll) {
      return this.WorkflowState_ConfirmRoll;
    }
    if (context.noDamage) return this.WorkflowState_WaitForSaves;
    const hasDamageRoll = this.activity.hasDamage || this.otherActivity?.hasDamage || this.activity.hasHealing || this.otherActivity?.hasHealing;
    if (!hasDamageRoll) return this.WorkflowState_WaitForSaves;
    if (context.attackRoll) return this.WorkflowState_AttackRollComplete;
    if (debugEnabled > 1) debug(`wait for damage roll has damage roll ${hasDamageRoll} isFumble ${this.isFumble} no auto damage ${this.noAutoDamage}`);
    if (checkMechanic("actionSpecialDurationImmediate"))
      expireMyEffects.bind(this)(["1Attack", "1Action", "1Spell"]);
    if (checkMechanic("actionSpecialDurationImmediate") && this.hitTargets.size)
      expireMyEffects.bind(this)(["1Hit"]);
    if (checkMechanic("actionSpecialDurationImmediate")) {
      expireMyEffects.bind(this)(["1Critical", "1Fumble"]);
    }
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("preDamageRoll"), "OnUse", "preDamageRoll");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("preDamageRoll"), "OnUse", "preDamageRoll");
    }
    if (this.shouldRollDamage) {
      if (debugEnabled > 0) warn("waitForDamageRoll | rolling damage ", this.rollOptions.event, configSettings.autoRollAttack, configSettings.autoFastForward);
      const storedData = this.chatCard?.getFlag("dnd5e", "item")?.data;
      if (storedData) {
        this.item = new CONFIG.Item.documentClass(storedData, { parent: this.actor });
      }
      this.rollOptions.isCritical = this.isCritical;
      this.rollOptions.fastForwardDamage = isAutoFastForward2("damage", { workflow: this });
      this.activity.rollDamage({ workflow: this, midiOptions: { ...this.rollOptions, workflowOptions: this.workflowOptions } }, {}, { create: false });
      return this.WorkflowState_Suspend;
    } else {
      const chatMessage = this.chatCard;
      if (chatMessage?.content) {
        const searchRe = /(<button[^>]*data-action="rollDamage"[^>]*>)(?:<span>)?[^<]*(?:<\/span>)?(<\/button>)/;
        const damageTypeString = this.activity.actionType === "heal" ? getHealingLabel() : i18n("DND5E.Damage");
        let damageString = this.rollOptions.isCritical || this.isCritical ? i18n("DND5E.Critical") : damageTypeString;
        if (this.rollOptions.fastForwardDamage && configSettings.showFastForward) damageString += ` ${i18n("midi-qol.fastForward")}`;
        let replaceString = `$1<span>${damageString}</span>$2`;
        let content = chatMessage.content.replace(searchRe, replaceString);
        await this.performThrottledUpdate(chatMessage, { content });
      }
    }
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_ConfirmRoll(context = {}) {
    if (context.attackRoll) return this.WorkflowState_AttackRollComplete;
    if (configSettings.confirmAttackDamage !== "none" && (this.activity.attack || this.activity.damage)) {
      await this.displayDamageRolls();
      return this.WorkflowState_Suspend;
    }
    return this.WorkflowState_DamageRollStarted;
  }
  async WorkflowState_RollConfirmed(context = {}) {
    expireMyEffects.bind(this)(["1Attack", "1Action", "1Spell"]);
    return this.WorkflowState_DamageRollStarted;
  }
  async WorkflowState_DamageRollStarted(context = {}) {
    if (this.itemCardUuid) {
      await _Workflow.removeItemCardButtons(this.itemCardUuid, { removeAllButtons: false, removeAttackButtons: getRemoveAttackButtons(this.activity), removeDamageButtons: getRemoveDamageButtons(this.activity), removeConfirmButtons: true });
    }
    if (getActivityAutoTargetAction2(this.activity) === "none" && activityHasAreaTarget(this.activity) && !this.activity.attack) {
      this.targets = validTargetTokens(new Set(game.user?.targets));
      this.hitTargets = validTargetTokens(new Set(game.user?.targets));
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      if (debugEnabled > 0) warn("damageRollStarted | for non auto target area effects spells", this);
    }
    const damageBonusMacros = this.getDamageBonusMacros();
    if (damageBonusMacros) {
      await this.rollBonusDamage(damageBonusMacros);
    }
    return this.WorkflowState_DamageRollComplete;
  }
  async WorkflowState_DamageRollComplete(context = {}) {
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("postDamageRoll"), "OnUse", "postDamageRoll");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("postDamageRoll"), "OnUse", "postDamageRoll");
    }
    await this.callMidiHooks("midi-qol.DamageRollComplete");
    if (this.aborted) return this.WorkflowState_Abort;
    if (this.hitTargets?.size || this.hitTargetsEC?.size) expireMyEffects.bind(this)(["1Hit"]);
    expireMyEffects.bind(this)(["1Action", "1Attack", "1Spell", "1Critical", "1Fumble"]);
    await this.displayDamageRolls();
    if (this.isFumble) {
      this.failedSaves = /* @__PURE__ */ new Set();
      this.hitTargets = /* @__PURE__ */ new Set();
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      return this.WorkflowState_ApplyDynamicEffects;
    }
    await _Workflow.removeItemCardButtons(this.itemCardUuid, { removeAllButtons: false, removeAttackButtons: getRemoveAttackButtons(this.activity), removeDamageButtons: getRemoveDamageButtons(this.activity), removeDnD5eButtons: false, removeDnd5eSaveButtons: false });
    return this.WorkflowState_WaitForSaves;
  }
  async WorkflowState_WaitForUtilityRoll(context = {}) {
    if (!this.activity.roll?.formula || context.utilityRolls) return this.WorkflowState_UtilityRollComplete;
    if (getAutoRollDamage(this) !== "none") {
      const rolls = await this.activity.rollFormula({ event: this.rollOptions.event, workflow: this, midiOptions: { ...this.rollOptions, workflowOptions: this.workflowOptions } }, {}, { create: false });
      if (!rolls || this.abort) {
        return this.WorkflowState_Abort;
      }
      return this.WorkflowState_UtilityRollComplete;
    }
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_UtilityRollComplete(context = {}) {
    if (this.itemCardUuid && this.utilityRolls) {
      this.displayUtilityRolls();
    }
    return this.WorkflowState_WaitForDamageRoll;
  }
  async WorkflowState_DamageRollCompleteCancelled(context = {}) {
    if (configSettings.undoWorkflow) {
    }
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_WaitForSaves(context = {}) {
    if (this.saveActivity === this.otherActivity && this.otherActivity) {
      if (this.activity.attack && this.hitTargets.size === 0 && this.hitTargetsEC.size === 0) {
        addUpdatesCacheFlags(this.id, { ["use.consumed" /* otherActivityConsumed */]: false }, "midi-qol");
        this.rawOtherDamageDetail = [];
        this.initSaveResults();
        return this.WorkflowState_SavesComplete;
      }
      const consumptionSuccess = await this.checkOtherActivityConsumption();
      if (!consumptionSuccess) {
        this.initSaveResults();
        return this.WorkflowState_SavesComplete;
      }
    }
    if (configSettings.allowUseMacro) {
      if (this.workflowOptions.noOnUseMacro !== true) await this.callMacros(this.item, this.onUseMacros?.getMacros("preSave"), "OnUse", "preSave");
      if (this.workflowOptions.noTargetOnUseMacro !== true) await this.triggerTargetMacros(["isAboutToSave"]);
      if (this.ammunition && this.workflowOptions.noOnUseMacro !== true) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("preSave"), "OnUse", "preSave");
    }
    if (this.workflowType === "BaseWorkflow" && !this.workflowOptions.ignoreUserTargets && !this.activity.attack && this.activity.target?.affects.type !== "self") {
      this.targets = new Set(game.user?.targets);
      this.hitTargets = new Set(this.targets);
    }
    this.failedSaves = new Set(this.hitTargets);
    if (!this.hasSave) {
      this.initSaveResults();
      return this.WorkflowState_SavesComplete;
    }
    await this.displaySaveTargets();
    this.initSaveResults();
    if (configSettings.autoCheckSaves !== "none") {
      await this.callMidiHooks("midi-qol.preCheckSaves");
      if (this.aborted) return this.WorkflowState_Abort;
      if (debugEnabled > 1) debug("Check Saves: renderChat message hooks length ", Hooks.events["renderChatMessageHTML"]?.length);
      let hookId = Hooks.on("renderChatMessageHTML", this.processSaveRoll.bind(this));
      let monksId = Hooks.on("updateChatMessage", this.monksSavingCheck.bind(this));
      let flashId = Hooks.on("flash-rolls-5e.rollComplete", this.flashSavingCheck.bind(this));
      try {
        const saveDisplay2 = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
        await this.checkSaves(saveDisplay2 !== "allShow");
      } catch (err) {
        const message = "midi-qol | checkSaves error";
        TroubleShooter.recordError(err, message);
        error2(message, err);
      } finally {
        Hooks.off("renderChatMessageHTML", hookId);
        Hooks.off("updateChatMessage", monksId);
        Hooks.off("flash-rolls-5e.rollComplete", flashId);
      }
      if (debugEnabled > 1) debug("Check Saves: ", this.saveRequests, this.saveTimeouts, this.saves);
      if (debugEnabled > 1) debug("Check Saves: renderChat message hooks length ", Hooks.events["renderChatMessageHTML"]?.length);
      await this.callMidiHooks("midi-qol.postCheckSaves");
      if (this.aborted) return this.WorkflowState_Abort;
      const saveDisplay = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
      await _Workflow.removeItemCardButtons(this.itemCardUuid, { removeDnd5eSaveButtons: configSettings.autoCheckSaves !== "none", removeDnd5eTemplateButtons: this.placedTemplateCount >= (Number(this.activity?.target?.template?.count) || 1) });
      await this.displaySaves(saveDisplay === "whisper");
    } else {
      this.effectTargets = this.failedSaves;
      return this.WorkflowState_RollFinished;
    }
    return this.WorkflowState_SavesComplete;
  }
  async WorkflowState_SavesComplete(context = {}) {
    expireMyEffects.bind(this)(["1Action", "1Spell"]);
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("postSave"), "OnUse", "postSave");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("postSave"), "OnUse", "postSave");
    }
    if (configSettings.keepRollStats && this.item && this.hasSave) {
      gameStats.addSaveResult(
        this.saves.size + this.failedSaves.size,
        this.saves.size,
        this.failedSaves.size,
        this.item
      );
    }
    return this.WorkflowState_AllRollsComplete;
  }
  async WorkflowState_AllRollsComplete(context = {}) {
    let items = [];
    const consumedFlag = this.chatCard?.getFlag(MODULE_ID, "use.consumed");
    if (consumedFlag === false) {
      this.otherDamageMatches = /* @__PURE__ */ new Set();
      this.rawOtherDamageDetail = [];
    } else {
      await this.buildOtherDamageMatches();
      if (this.otherDamageMatches.size > 0) {
        await this.checkOtherActivityConsumption();
      }
    }
    if (this.rawDamageDetail && (this.rawDamageDetail?.length || this.rawOtherDamageDetail?.length)) await processDamageRoll(this, this.rawDamageDetail[0]?.type ?? this.defaultDamageType);
    this.recordDamageStats();
    if (configSettings.allowUseMacro && this.workflowOptions.noTargetOnUseMacro !== true && !configSettings.autoApplyDamage.includes("Card"))
      await this.triggerTargetMacros(["isDamaged"], this.hitTargets);
    if (debugEnabled > 1) debug("all rolls complete ", foundry.utils.duplicate(this.rawDamageDetail));
    return this.WorkflowState_ApplyDynamicEffects;
  }
  /**
   * Check and process consumption for the other activity.
   * This should be called either before the other activity save is rolled,
   * or before applying other activity damage - but only once.
   * @returns true if consumption succeeded or was not needed, false if it failed or was cancelled
   */
  async checkOtherActivityConsumption() {
    if (!this.otherActivity || !this.otherActivity.hasConsumption()) {
      return true;
    }
    const existingFlag = this.chatCard?.getFlag(MODULE_ID, "use.consumed");
    if (existingFlag !== void 0) {
      return existingFlag !== false;
    }
    const shouldShowDialog = !this.otherActivity.shouldAutoConsume(this);
    let otherUsageConfig = this.otherActivity._prepareUsageConfig({
      consume: { resources: true, spellSlot: false, action: false }
    });
    if (shouldShowDialog) {
      try {
        const DialogClass = this.otherActivity.metadata?.usage?.dialog ?? game.dnd5e.applications.activity.ActivityUsageDialog;
        const canScale = this.otherActivity.canScale ?? this.otherActivity.consumption?.scaling?.allowed ?? false;
        otherUsageConfig = await DialogClass.create(this.otherActivity, otherUsageConfig, {
          display: { consumption: true, scaling: canScale, concentration: false, creation: false }
        });
        const messageConfig = { data: {} };
        const consumeResult = await this.otherActivity.consume(otherUsageConfig, messageConfig);
        if (consumeResult === false) {
          this.otherDamageMatches = /* @__PURE__ */ new Set();
          this.rawOtherDamageDetail = [];
          addUpdatesCacheFlags(this.id, { ["use.consumed" /* otherActivityConsumed */]: false }, "midi-qol");
          return false;
        }
        const consumed = getConsumptionData(messageConfig) ?? {};
        const scaling = otherUsageConfig.scaling;
        if (this.itemCardUuid) {
          addUpdatesCacheFlags(this.id, {
            ["use.consumed" /* otherActivityConsumed */]: consumed,
            ["use.otherScaling" /* otherActivityScaling */]: scaling
          }, "midi-qol");
        }
      } catch (e) {
        this.otherDamageMatches = /* @__PURE__ */ new Set();
        this.rawOtherDamageDetail = [];
        addUpdatesCacheFlags(this.id, { ["use.consumed" /* otherActivityConsumed */]: false }, "midi-qol");
        return false;
      }
    } else {
      const consumptionCheck = await this.otherActivity._prepareUsageUpdates(otherUsageConfig, { returnErrors: true });
      if (Array.isArray(consumptionCheck) && consumptionCheck.length > 0) {
        ui.notifications?.warn(i18nFormat2("midi-qol.otherActivityConsumptionFailed", {
          actorName: this.actor?.name ?? "Unknown",
          itemName: this.item?.name ?? "Unknown",
          activityName: this.activity?.name ?? "Unknown"
        }));
        this.otherDamageMatches = /* @__PURE__ */ new Set();
        this.rawOtherDamageDetail = [];
        addUpdatesCacheFlags(this.id, { ["use.consumed" /* otherActivityConsumed */]: false }, "midi-qol");
        return false;
      }
      const messageConfig = { data: {} };
      const consumeResult = await this.otherActivity.consume(otherUsageConfig, messageConfig);
      if (consumeResult === false) {
        this.otherDamageMatches = /* @__PURE__ */ new Set();
        this.rawOtherDamageDetail = [];
        addUpdatesCacheFlags(this.id, { ["use.consumed" /* otherActivityConsumed */]: false }, "midi-qol");
        return false;
      }
      const consumed = getConsumptionData(messageConfig) ?? {};
      const scaling = otherUsageConfig.scaling;
      if (this.itemCardUuid) {
        addUpdatesCacheFlags(this.id, {
          ["use.consumed" /* otherActivityConsumed */]: consumed,
          ["use.otherScaling" /* otherActivityScaling */]: scaling
        }, "midi-qol");
      }
    }
    await this.rollDeferredOtherDamage();
    return true;
  }
  /**
   * Build otherDamageMatches if not already populated.
   * Evaluates the otherActivity useCondition per-target exactly once.
   */
  async buildOtherDamageMatches() {
    if (this.otherDamageMatches !== void 0) return;
    this.otherDamageMatches = /* @__PURE__ */ new Set();
    const otherCondition = this.otherUseCondition;
    const targetsForOtherDamage = this.activity.attack ? /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]) : this.targets;
    for (const token of targetsForOtherDamage) {
      if (otherCondition) {
        if (await evalActivationCondition2(this, otherCondition, token, { async: true, errorReturn: false }))
          this.otherDamageMatches.add(token);
      } else {
        this.otherDamageMatches.add(token);
      }
    }
  }
  /**
   * Roll the other activity damage that was deferred until after consumption was confirmed.
   */
  async rollDeferredOtherDamage() {
    const config = this.otherDamageConfig;
    if (!config || !this.otherActivity) return;
    const dialog = this.otherDamageDialog ?? {};
    try {
      const otherResult = await this.activity.rollOtherDamage(config, dialog);
      if (otherResult) {
        await this.displayDamageRolls();
      }
    } catch (err) {
      TroubleShooter.recordError(err, "midi-qol | rollDeferredOtherDamage error");
      error2("rollDeferredOtherDamage error", err);
    }
    this.otherDamageConfig = void 0;
    this.otherDamageDialog = void 0;
  }
  async WorkflowState_ApplyDynamicEffects(context = {}) {
    this.activationMatches = /* @__PURE__ */ new Set();
    this.otherActivationMatches = /* @__PURE__ */ new Set();
    this.activationFails = /* @__PURE__ */ new Set();
    this.otherActivationFails = /* @__PURE__ */ new Set();
    let targetsToUse = this.targets;
    if (this.activity.target?.affects?.type === "" && this.targets.size == 0)
      targetsToUse = new Set(game.user?.targets);
    for (let token of targetsToUse) {
      const effectCondition = this.effectActivationCondition;
      const otherEffectCondition = this.otherEffectActivationCondition;
      if (effectCondition) {
        if (await evalActivationCondition2(this, effectCondition, token, { async: true, errorReturn: true })) {
          this.activationMatches.add(token);
        } else
          this.activationFails.add(token);
      } else this.activationMatches.add(token);
      if (otherEffectCondition) {
        if (await evalActivationCondition2(this, otherEffectCondition, token, { async: true, errorReturn: true })) {
          this.otherActivationMatches.add(token);
        } else
          this.otherActivationFails.add(token);
      } else this.otherActivationMatches.add(token);
    }
    expireMyEffects.bind(this)(["1Action", "1Spell"]);
    this.effectTargets = /* @__PURE__ */ new Set();
    this.otherEffectTargets = /* @__PURE__ */ new Set();
    this.allOtherEffectTargets = new Set(this.targets);
    this.effectTargetsOnSave = this.activationMatches;
    this.otherEffectTargetsOnSave = this.otherActivationMatches;
    if (this.forceApplyEffects) {
      this.effectTargets = /* @__PURE__ */ new Set();
    } else if ((this.saveActivity?.save || this.saveActivity?.check) && this.activity.attack) {
      this.effectTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
      this.effectTargets = new Set([...this.effectTargets].filter((t) => this.failedSaves.has(t)));
    } else if (this.activity.save || this.activity.check) this.effectTargets = this.failedSaves;
    else if (this.activity.attack) {
      this.effectTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
    } else
      this.effectTargets = this.targets;
    this.effectTargets = this.effectTargets.filter((t) => this.activationMatches.has(t));
    if (this.forceApplyEffects) {
      this.otherEffectTargets = this.targets;
    } else if ((this.otherActivity?.save || this.otherActivity?.check) && this.activity.attack) {
      this.otherEffectTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
      if (!this.otherEffectActivationCondition)
        this.otherEffectTargets = new Set([...this.otherEffectTargets].filter((t) => this.failedSaves.has(t)));
    } else if (this.otherActivity?.save || this.otherActivity?.check) this.otherEffectTargets = this.failedSaves;
    else if (this.activity.attack) {
      this.otherEffectTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
    } else
      this.otherEffectTargets = this.targets;
    this.otherEffectTargets = this.otherEffectTargets.filter((t) => this.otherActivationMatches.has(t));
    if (this.otherActivity && this.otherEffectTargets.size > 0) {
      const otherConsumedFlag = this.chatCard?.getFlag(MODULE_ID, "use.consumed");
      if (otherConsumedFlag === false) {
        this.otherEffectTargets = /* @__PURE__ */ new Set();
      } else if (otherConsumedFlag === void 0 && this.otherActivity.hasConsumption()) {
        const consumptionSuccess = await this.checkOtherActivityConsumption();
        if (!consumptionSuccess) {
          this.otherEffectTargets = /* @__PURE__ */ new Set();
        }
      }
    }
    const specialExpiries = [
      "isDamaged",
      "isHealed",
      "isSaveSuccess",
      "isSaveFailure",
      "isSave",
      "isHit"
    ];
    await this.expireTargetEffects(specialExpiries);
    if (configSettings.autoItemEffects === "off" && !this.forceApplyEffects) return this.WorkflowState_RollFinished;
    if (this.activity.item) {
      if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
        let results = await this.callMacros(this.activity.item, this.onUseMacros?.getMacros("preActiveEffects"), "OnUse", "preActiveEffects");
        if (results.some((r) => r?.haltEffectsApplication))
          return this.WorkflowState_RollFinished;
        results = await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("preActiveEffects"), "OnUse", "preActiveEffects");
        if (results.some((r) => r?.haltEffectsApplication))
          return this.WorkflowState_RollFinished;
      }
    }
    let useCE = configSettings.autoCEEffects;
    if (!this.activity.item) return this.WorkflowState_RollFinished;
    if (this.activity.midiProperties?.autoCEEffects && this.activity.midiProperties.autoCEEffects !== "default") {
      useCE = this.activity.midiProperties.autoCEEffects;
    }
    let ceEffect = getCEEffectByName2(this.activity.name);
    if (!ceEffect) ceEffect = getCEEffectByName2(this.activity.item.name);
    const isApplicableEffect = /* @__PURE__ */ __name((activity, efData) => {
      if (!efData || !activity) return false;
      const level = activity.relevantLevel;
      if ((efData.level?.min ?? -Infinity) > level || level > (efData.level?.max ?? Infinity))
        return false;
      return !!efData.effect && efData.effect?.transfer !== true && efData.effect?.type !== "enchantment";
    }, "isApplicableEffect");
    let activityEffects = (this.activity.effects ?? []).filter((efData) => isApplicableEffect(this.activity, efData) && efData.onSave !== true).map((efData) => efData.effect);
    let onSaveActivityEffects = (this.activity.effects ?? []).filter((efData) => isApplicableEffect(this.activity, efData) && efData.onSave === true).map((efData) => efData.effect);
    let otherActivityEffects = (this.otherActivity?.effects ?? []).filter((efData) => isApplicableEffect(this.otherActivity, efData) && efData.onSave !== true).map((efData) => efData.effect);
    let onSaveOtherActivityEffects = (this.otherActivity?.effects ?? []).filter((efData) => isApplicableEffect(this.otherActivity, efData) && efData.onSave === true).map((efData) => efData.effect);
    if (activityEffects.length > 0 && this.effectTargets.size > 0 && this.activity.midiProperties?.chooseEffects) {
      activityEffects = await this.chooseEffects(activityEffects, { activity: this.activity, activityUuid: this.activity.uuid, isOtherActivity: false });
    }
    if (otherActivityEffects.length > 0 && this.otherEffectTargets.size > 0 && this.otherActivity?.midiProperties?.chooseEffects) {
      otherActivityEffects = await this.chooseEffects(otherActivityEffects, { activity: this.otherActivity, activityUuid: this.otherActivity.uuid, isOtherActivity: true });
    }
    const ceTargetEffect = ceEffect && !(ceEffect?.flags?.dae?.selfTarget || ceEffect?.flags?.dae?.selfTargetAlways);
    const ceSelfEffect = ceEffect && (ceEffect?.flags?.dae?.selfTarget || ceEffect?.flags?.dae?.selfTargetAlways);
    const hasActivityEffects = hasDAE(this) && activityEffects.length > 0;
    const hasOtherActivityEffects = hasDAE(this) && otherActivityEffects.length > 0;
    const activitySelfEffects = [...activityEffects, ...onSaveActivityEffects].filter((ef) => ef.flags?.dae?.selfTarget || ef.flags?.dae?.selfTargetAlways) ?? [];
    const activitySelfAllEffect = [...activityEffects, ...onSaveActivityEffects].filter((ef) => ef.flags?.dae?.selfTargetAlways) ?? [];
    const otherActivitySelfEffects = [...otherActivityEffects, ...onSaveOtherActivityEffects].filter((ef) => ef.flags?.dae?.selfTarget || ef.flags?.dae?.selfTargetAlways) ?? [];
    const otherActivitySelfAllEffects = [...otherActivityEffects, ...onSaveOtherActivityEffects].filter((ef) => ef.flags?.dae?.selfTargetAlways) ?? [];
    activityEffects = activityEffects.filter((ef) => !ef.flags?.dae?.selfTarget && !ef.flags?.dae?.selfTargetAlways);
    otherActivityEffects = otherActivityEffects.filter((ef) => !ef.flags?.dae?.selfTarget && !ef.flags?.dae?.selfTargetAlways);
    onSaveActivityEffects = onSaveActivityEffects.filter((ef) => !ef.flags?.dae?.selfTarget && !ef.flags?.dae?.selfTargetAlways);
    onSaveOtherActivityEffects = onSaveOtherActivityEffects.filter((ef) => !ef.flags?.dae?.selfTarget && !ef.flags?.dae?.selfTargetAlways);
    let selfEffectsToApply = "none";
    const metaData = {
      "flags": {
        "dae": { transfer: false },
        "midi-qol": {
          castData: this.castData
        }
      }
    };
    let origin = this.actor.effects.get(this.chatCard?.getFlag("dnd5e", "use.concentrationId") ?? "")?.uuid ?? getConcentrationEffect(this.actor, this.item)?.uuid;
    let damageComponents = {};
    let damageListItem;
    let hpDamage;
    let totalDamage;
    const doEffectsData = {
      damageTotal: 0,
      damage: 0,
      critical: this.isCritical,
      fumble: this.isFumble,
      itemCardUuid: this.itemCardUuid,
      metaData,
      selfEffects: "none",
      spellLevel: this.spellLevel,
      origin: "",
      toggleEffect: this.activity.midiProperties?.toggleEffect,
      // Is this used?
      tokenUuid: this.tokenUuid,
      // Is this used?
      actorUuid: this.actor.uuid,
      whisper: false,
      // Is this used?
      workflowOptions: this.workflowOptions,
      context: {
        damageComponents: {},
        damageTotal: 0,
        damageApplied: 0,
        otherDamage: this.otherDamageTotal ?? 0,
        bonusDamage: this.bonusDamageTotal ?? 0,
        itemData: this.item?.getRollData?.()?.item,
        activity: this.activity?.getRollData?.()?.activity,
        flags: {
          dnd5e: {
            scaling: this.chatCard?.getFlag("dnd5e", "scaling"),
            spellLevel: this.chatCard?.getFlag("dnd5e", "use.spellLevel")
          }
        },
        utilityRollTotal: this.utilityRoll?.total
      }
    };
    for (let token of this.targets) {
      const tokenDamages = this.damageList?.find((di) => di.targetUuid === getTokenDocument(token)?.uuid);
      if (tokenDamages) {
        doEffectsData.damageTotal = tokenDamages.totalDamage;
        doEffectsData.context.damageTotal = tokenDamages.totalDamage;
        doEffectsData.damage = tokenDamages.hpDamage;
        doEffectsData.context.damageApplied = tokenDamages.hpDamage;
        doEffectsData.context.damageComponents = [tokenDamages.damageDetails["combinedDamage"], configSettings.singleConcentrationRoll ? [] : tokenDamages.damageDetails["otherDamage"] ?? []].reduce((summary, damages) => {
          let damagesSummary = damages.reduce((damageComponents2, damageEntry) => {
            damageComponents2[damageEntry.type] = damageEntry.value + (damageComponents2[damageEntry.type] ?? 0);
            return damageComponents2;
          }, {});
          Object.keys(damagesSummary).forEach((key) => {
            summary[key] = damagesSummary[key] + (summary[key] ?? 0);
          });
          return summary;
        }, {});
      }
      if (this.forceApplyEffects || this.effectTargets.has(token)) {
        if (hasActivityEffects && (!ceTargetEffect || ["none", "both", "itempri"].includes(useCE))) {
          let selectedEffects = activityEffects;
          if (!this.forceApplyEffects) {
            selectedEffects = activityEffects.filter(
              (ef) => this.effectTargets?.has(token) && (!(this.activity.save || this.activity.check) || this.failedSaves.has(token) || this.activity.effects.some((effectDetail) => effectDetail.onSave == true && effectDetail._id === ef.id))
            );
          }
          const effectsToApplyUuids = selectedEffects.map((ef) => ef.uuid);
          if (effectsToApplyUuids?.length > 0) {
            doEffectsData.origin = origin ?? effectsToApplyUuids[0];
            await globalThis.DAE.doActivityEffects(this.activity, true, [token], effectsToApplyUuids, doEffectsData);
          }
        }
        if (ceTargetEffect && this.activity.item && token.actor) {
          if (ceEffect && (["both", "cepri", "ceOnly"].includes(useCE) || useCE === "itempri" && !hasActivityEffects)) {
            const targetHasEffect = token.actor.effects.find((ef) => ef.name === ceEffect.name);
            if (this.activity.midiProperties?.toggleEffect && targetHasEffect) {
              await CEToggleEffect({ effectName: ceEffect.name, uuid: token.actor.uuid, origin });
            } else {
              let removeExisting = ["none", "noneName"].includes(ceEffect.flags?.dae?.stackable ?? "none");
              const hasExisting = await CEHasEffectApplied({ effectName: ceEffect.name, uuid: token.actor.uuid });
              if (removeExisting && hasExisting) {
                await CERemoveEffect({ effectName: ceEffect.name, uuid: token.actor.uuid, origin });
              }
              const effectData = foundry.utils.mergeObject(ceEffect.toObject(), metaData);
              if (isInCombat(token.actor) && (effectData.duration?.seconds ?? 0) <= 60) {
                effectData.duration.rounds = effectData.duration.rounds ?? Math.ceil((effectData.duration?.seconds ?? 0) / CONFIG.time.roundTime);
                delete effectData.duration.seconds;
              }
              effectData.origin = origin ?? this.activity.uuid ?? this.activity.item.uuid;
              const concentrationUuid = this.actor.effects.get(this.chatCard?.getFlag("dnd5e", "use.concentrationId") ?? "")?.uuid;
              if (concentrationUuid) {
                foundry.utils.setProperty(effectData, "flags.dnd5e.dependentOn", concentrationUuid);
              }
              const effects = await CEAddEffectWith2({ effectData, effectName: ceEffect.name, uuid: token.actor.uuid, origin, overlay: false });
            }
          }
        }
      }
      if (this.effectTargetsOnSave.size > 0 && onSaveActivityEffects.length > 0) {
        let selectedEffects = onSaveActivityEffects;
        const effectsToApplyUuids = onSaveActivityEffects.map((ef) => ef.uuid);
        if (effectsToApplyUuids?.length > 0) {
          doEffectsData.origin = effectsToApplyUuids[0];
          await globalThis.DAE.doActivityEffects(this.activity, true, [token], effectsToApplyUuids, doEffectsData);
        }
      }
      if (this.forceApplyEffects || this.otherEffectTargets.has(token)) {
        if (hasOtherActivityEffects) {
          let selectedEffects = otherActivityEffects;
          if (!this.forceApplyEffects) {
            selectedEffects = otherActivityEffects.filter(
              (ef) => this.otherActivationMatches?.has(token) && this.otherEffectTargets?.has(token) && (!(this.otherActivity?.save || this.otherActivity?.check) || this.failedSaves.has(token) || this.otherActivity?.effects.some((effectDetail) => effectDetail.onSave == true && effectDetail._id === ef.id))
            );
          }
          const effectsToApplyUuids = selectedEffects.map((ef) => ef.uuid);
          if (effectsToApplyUuids?.length > 0) {
            doEffectsData.origin = effectsToApplyUuids[0];
            await globalThis.DAE.doActivityEffects(this.otherActivity, true, [token], effectsToApplyUuids, doEffectsData);
          }
        }
      }
      if (this.otherEffectTargetsOnSave.size > 0 && onSaveOtherActivityEffects.length > 0) {
        let selectedEffects = onSaveOtherActivityEffects;
        const effectsToApplyUuids = onSaveOtherActivityEffects.map((ef) => ef.uuid);
        if (effectsToApplyUuids?.length > 0) {
          doEffectsData.origin = effectsToApplyUuids[0];
          await globalThis.DAE.doActivityEffects(this.otherActivity, true, [token], effectsToApplyUuids, doEffectsData);
        }
      }
    }
    if (configSettings.allowUseMacro && this.workflowOptions.noTargetOnUseMacro !== true) await this.triggerTargetMacros(["postTargetEffectApplication"], this.targets);
    let selfEffects = [];
    if (this.effectTargets?.size > 0) selfEffects = activitySelfEffects;
    else selfEffects = activitySelfAllEffect;
    const selfToken = getOrCreateTokenForActor(this.actor);
    if (selfEffects.length > 0 && selfToken) {
      doEffectsData.origin = origin ?? this.activity.uuid ?? this.item.uuid;
      await globalThis.DAE.doActivityEffects(this.activity, true, [selfToken], selfEffects.map((ef) => ef.uuid), doEffectsData);
    }
    let otherSelfEffects = [];
    if (this.otherEffectTargets.size > 0) otherSelfEffects = otherActivitySelfAllEffects;
    else otherSelfEffects = otherActivitySelfEffects;
    if (otherSelfEffects.length > 0 && selfToken) {
      doEffectsData.origin = this.otherActivity.item.uuid;
      await globalThis.DAE.doActivityEffects(this.otherActivity, true, [selfToken], otherSelfEffects.map((ef) => ef.uuid), doEffectsData);
    }
    if (this.actor && ceSelfEffect && (this.effectTargets?.size > 0 || ceEffect.flags?.dae?.selfTargetAlways)) {
      if (["both", "cepri"].includes(useCE) || useCE === "itempri" && !selfEffects.length) {
        const actorHasEffect = this.actor.effects.find((ef) => ef.name === this.activity.name);
        if (this.activity.midiProperties?.toggleEffect && actorHasEffect) {
          CEToggleEffect({ effectName: this.activity.name, uuid: this.actor.uuid, origin });
        } else {
          if ((ceEffect.flags?.dae?.stackable ?? "none") === "none" && await CEHasEffectApplied({ effectName: this.activity.name, uuid: this.actor.uuid })) {
            await CERemoveEffect({ effectName: this.activity.name, uuid: this.actor.uuid, origin });
          }
          const effectData = foundry.utils.mergeObject(ceEffect.toObject(), metaData);
          effectData.origin = origin;
          const concentrationUuid = this.actor.effects.get(this.chatCard?.getFlag("dnd5e", "use.concentrationId") ?? "")?.uuid;
          if (concentrationUuid) {
            foundry.utils.setProperty(effectData, "flags.dnd5e.dependentOn", concentrationUuid);
          }
          const effects = await CEAddEffectWith2({ effectData, effectName: this.activity.name, uuid: this.actor.uuid, origin, overlay: false });
        }
      }
    }
    return this.WorkflowState_RollFinished;
  }
  async WorkflowState_Cleanup(context = {}) {
    const blfxActive = game.modules.get("boss-loot-assets-premium")?.active || game.modules.get("boss-loot-assets-free")?.active;
    const AAActive = game.modules.get("autoanimations")?.active;
    const elapsedTimeRequired = 5e3;
    if (configSettings.autoRemoveInstantaneousTemplate && this.templateUuids.length > 0 && this.activity.duration?.units === "inst") {
      for (const tUuid of this.templateUuids) {
        if (!blfxActive) {
          const elapsed = Date.now() - (this.workflowStartTime ?? 0);
          let timeout = AAActive ? Math.max(elapsedTimeRequired - elapsed, 1) : 1;
          timeout = Math.max(elapsedTimeRequired, timeout);
          setTimeout(() => {
            const templateToDelete = fromUuidSync(tUuid) || null;
            templateToDelete?.delete();
          }, timeout);
        } else {
          setTimeout(() => {
            const templateToDelete = fromUuidSync(tUuid) || null;
            templateToDelete?.delete();
          }, elapsedTimeRequired);
          log("Not auto removing instantaneous template because boss loot assets active");
        }
      }
    }
    if (this.postSummonHookId) Hooks.off("dnd5e.postSummon", this.postSummonHookId);
    if (this.activity.target?.affects.type !== "" || this.targets.size !== 0) {
    }
    if (this.selfTargeted) popTargetSave();
    return this.WorkflowState_Completed;
  }
  async WorkflowState_Completed(context = {}) {
    if (this.itemCardUuid && this.chatCard) {
      if (this.damageList && saveToChatCard && this.itemCardUuid) {
        const damageListData = prepareDamageListToJSON(this.damageList);
        damageListData.forEach((di) => delete di.damageDetails);
        addUpdatesCacheFlags(this.id, { ["damageList" /* damageList */]: damageListData }, "midi-qol");
      }
      await _Workflow.removeItemCardButtons(this.itemCardUuid, { removeAllButtons: getRemoveAllButtons(this.activity), removeAttackButtons: getRemoveAttackButtons(this.activity), removeDamageButtons: getRemoveDamageButtons(this.activity), removeDnD5eButtons: getRemoveAllButtons(this.activity) && configSettings.autoCheckSaves !== "none", removeDnd5eSaveButtons: configSettings.autoCheckSaves !== "none" });
    }
    if (context.attackRoll) return this.WorkflowState_AttackRollComplete;
    if (context.damageRoll) return this.WorkflowState_ConfirmRoll;
    if (debugEnabled > 0) warn("Workflow complete ", this.id, "elapsed time", Date.now() - (this.workflowStartTime ?? 0));
    warn("Workflow complete ", this.id, "elapsed time", Date.now() - (this.workflowStartTime ?? 0));
    let shouldTrigger = false;
    if (this.activity.midiProperties?.triggeredActivityId && !this.aborted) {
      let activity = this.activity;
      shouldTrigger = true;
      let possibleTriggerActivity = await this.activity.getTriggeredActivity();
      const visitedActivities = /* @__PURE__ */ new Set([activity.uuid]);
      while (possibleTriggerActivity) {
        if (visitedActivities.has(possibleTriggerActivity.uuid)) {
          ui.notifications?.error(`midi-qol | loop detected in triggered activities for ${this.actor.name} ${this.item.name} ${this.activity.name} - execution aborted`);
          shouldTrigger = false;
          break;
        }
        visitedActivities.add(possibleTriggerActivity.uuid);
        possibleTriggerActivity = await possibleTriggerActivity.getTriggeredActivity();
      }
      let triggeredActivity = await this.activity.getTriggeredActivity();
      if (shouldTrigger && triggeredActivity) {
        if (shouldTrigger) {
          let usage = { midiOptions: {} };
          usage.midiOptions.event = this.rollOptions.event;
          usage.midiOptions.workflowOptions = this.workflowOptions;
          usage.midiOptions.rollOptions = this.rollOptions;
          usage.midiOptions.isTriggered = true;
          let targetUuids = [];
          const toUuids = /* @__PURE__ */ __name((targets) => {
            return Array.from(targets ?? /* @__PURE__ */ new Set()).map((t) => (t instanceof TokenDocument ? t : t.document).uuid);
          }, "toUuids");
          if (this.activity.midiProperties?.triggeredActivityRollAs) {
            switch (this.activity.midiProperties.triggeredActivityRollAs) {
              case "firstTarget":
                usage.midiOptions.rollAs = getActor(this.targets.first());
                break;
              case "firstHitTarget":
                usage.midiOptions.rollAs = getActor(this.hitTargets.first());
                break;
              case "firstMissedTarget":
                usage.midiOptions.rollAs = getActor(this.targets.difference(this.hitTargets).first());
                break;
              case "firstSaveTarget":
                usage.midiOptions.rollAs = getActor(this.saves.first());
                break;
              case "firstFailedSaveTarget":
                usage.midiOptions.rollAs = getActor(this.failedSaves.first());
                break;
              case "self":
              default:
                usage.midiOptions.rollAs = this.actor;
                break;
            }
          }
          if (!usage.midiOptions.rollAs?.isOwner) usage.midiOptions.checkGMstatus = true;
          if (this.activity.midiProperties?.triggeredActivityTargets)
            switch (this.activity.midiProperties.triggeredActivityTargets) {
              case "self":
                targetUuids = [this.tokenUuid ?? ""];
                break;
              case "hitTargets":
                targetUuids = toUuids(this.hitTargets);
                break;
              case "missedTargets":
                targetUuids = toUuids(this.targets.difference(this.hitTargets));
                break;
              case "failedSaves":
                targetUuids = toUuids(this.failedSaves);
                break;
              case "saveTargets":
                targetUuids = toUuids(this.saves);
                break;
              case "targets":
                targetUuids = toUuids(this.targets);
                break;
              case "retarget":
              default:
                break;
            }
          usage.midiOptions.targetUuids = targetUuids;
          usage.midiOptions.triggeredActivity = true;
          if (this.activity.midiProperties.triggeredActivityTargets !== "retarget" && targetUuids?.length === 0)
            shouldTrigger = false;
          if (this.activity.midiProperties?.triggeredActivityConditionText && shouldTrigger) {
            const target = await fromUuid(targetUuids[0]);
            ;
            const condition = this.activity.midiProperties?.triggeredActivityConditionText;
            const conditionData = createConditionData({ workflow: this, activity: this.activity, item: this.item, target });
            shouldTrigger = await evalCondition(condition, conditionData, { async: true, errorReturn: false });
          }
          if (shouldTrigger) {
            foundry.utils.setProperty(usage, "midiOptions.isCritical", this.isCritical);
            foundry.utils.setProperty(usage, "midiOptions.workflowOptions.triggeringWorkflowId", this.id);
            foundry.utils.setProperty(usage, "midiOptions.workflowOptions.triggeringActivityUuid", this.activity.uuid);
            const triggerConsume = this.activity.midiProperties?.triggeredActivityConsume ?? true;
            const triggerConfigure = this.activity.midiProperties?.triggeredActivityConfigure ?? true;
            if (!triggerConsume) foundry.utils.setProperty(usage, "consume", { resources: false, spellSlot: false, action: false });
            await completeActivityUse2(triggeredActivity, usage, { configure: triggerConfigure }, {});
          }
        }
      }
    }
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_Abort(context = {}) {
    this.aborted = true;
    if (this.postSummonHookId) Hooks.off("dnd5e.postSummon", this.postSummonHookId);
    if (!this.keepActivityCard && this.itemCardUuid && fromUuidSync(this.itemCardUuid)) {
      await this.chatCard.delete();
      clearUpdatesCache(this.itemCardUuid);
    }
    for (const tUuid of this.templateUuids) {
      const templateToDelete = await fromUuid(tUuid);
      if (templateToDelete) await templateToDelete.delete();
    }
    return this.WorkflowState_Cleanup;
  }
  async WorkflowState_Cancel(context = {}) {
    if (configSettings.undoWorkflow) await unTimedExecuteAsGM("undoTillWorkflow", this.uuid, true, true);
    return this.WorkflowState_Abort;
  }
  async WorkflowState_RollFinished(context = {}) {
    if (this.aborted) {
      const message = `${this.workflowName} Workflow ${this.id} RollFinished called when aborted`;
      error2(message);
      TroubleShooter.recordError(new Error(message), message);
    }
    const specialExpiries = [
      "isDamaged",
      "isHealed",
      "1Reaction"
    ];
    await this.expireTargetEffects(specialExpiries);
    let chatMessage = this.chatCard;
    let selfTarget = this.token ?? getOrCreateTokenForActor(this.actor);
    if (this.needsHitTargetsDisplayed && this.targets?.size > 0 && chatMessage && (this.activity.effects?.length || this.otherActivity?.effects?.length)) {
      this.hitDisplayData = {};
      const theTargets = this.targets;
      for (let targetToken of theTargets) {
        if (!targetToken.actor) continue;
        const targetTokenUuid = targetToken.actor?.uuid;
        if (!targetTokenUuid) continue;
        let img = targetToken.document?.texture.src ?? targetToken.actor.img;
        if (configSettings.usePlayerPortrait && targetToken.actor.type === "character") {
          img = targetToken.actor.img ?? targetToken.document?.texture.src;
        }
        if (foundry.helpers.media.VideoHelper.hasVideoExtension(img ?? "")) {
          img = await game.video?.createThumbnail(img ?? "", { width: 100, height: 100 });
        }
        this.hitDisplayData[targetTokenUuid] = {
          targetClass: targetToken.actor.hasPlayerOwner ? "midi-qol-player-target" : "midi-qol-nonplayer-target",
          target: targetToken,
          hitClass: "success",
          acClass: "",
          img: img ?? "",
          gmName: getTokenName(targetToken),
          playerName: getTokenPlayerName(targetToken) ?? "",
          uuid: targetToken.document.uuid,
          showAC: false,
          isHit: true
        };
      }
      await this.displayHits(chatMessage.whisper.length > 0, true);
      chatMessage = this.chatCard;
    }
    let content = chatMessage?.content;
    let contentChanged = false;
    if (content && getRemoveAttackButtons(this.activity) && chatMessage && configSettings.confirmAttackDamage === "none") {
      let searchRe = /<button[^>]*?data-action="rollAttack"[\s\S]*?<\/button>/;
      content = content.replace(searchRe, "");
      contentChanged = true;
    }
    if (content) {
      let isEmanationTargeting = activityHasAutoPlaceTemplate(this.activity) || activityHasEmanationNoTemplate(this.activity);
      let isAoETargeting = !isEmanationTargeting && activityHasAreaTarget(this.activity);
      if (isAoETargeting && !isEmanationTargeting && this.placedTemplateCount >= (Number(this.activity?.target?.template?.count) || 1)) {
        const searchRe = /<button[^>]*?data-action="placeTemplate"[\s\S]*?<\/button>/;
        content = content.replace(searchRe, "");
        contentChanged = true;
      }
    }
    if (this.activity.target?.affects.type !== "" || this.targets.size !== 0) {
      if (configSettings.autoItemEffects === "applyRemove" && content) {
        const buttonRe = /<button[^>]*?data-action="midiApplyEffects"[\s\S]*?<\/button>/;
        content = content?.replace(buttonRe, "");
        contentChanged = true;
      }
    }
    if (contentChanged) {
      const update = {
        "content": content,
        flags: { "midi-qol": { type: MESSAGE_TYPES.ITEM } }
      };
      await this.performThrottledUpdate(chatMessage, update);
    }
    if (configSettings.autoRemoveSummonedCreature && this.summonedCreatures && this.summonedCreatures.length > 0) {
      let origin = getConcentrationEffect(this.actor, this.item);
      if (origin) {
        for (let sc of this.summonedCreatures) {
          sc.setFlag("dnd5e", "dependentOn", origin.uuid);
        }
      } else {
        const activityDuration = this.activity.duration;
        let effectData;
        const summonString = i18n("DND5E.ActionSumm") + ": ";
        let effect = this.item.actor?.effects.find((ef) => ef.name === summonString + this.item.name);
        if (effect) {
          for (let sc of this.summonedCreatures) {
            sc.setFlag("dnd5e", "dependentOn", effect.uuid);
          }
        } else {
          effectData = {
            origin: this.item.uuid,
            //flag the effect as associated to the spell being cast
            disabled: false,
            img: this.item.img,
            name: summonString + this.item.name,
            duration: {},
            flags: {
              dae: {
                stackable: "noneName"
              }
            }
          };
          const inCombat2 = !!game.combat?.turns.some((combatant) => combatant.token?.id === selfTarget?.id);
          const converted = convertDuration(activityDuration, inCombat2);
          if (converted.type === "none") {
            effectData.duration = {
              startRound: game.combat?.round,
              startTurn: game.combat?.turn,
              startTime: game.time?.worldTime
            };
          } else if (converted.type === "seconds") {
            effectData.duration = { seconds: Math.max(converted.seconds ?? 1, 1), startTime: game.time?.worldTime };
          } else if (converted.type === "turns") {
            effectData.duration = {
              rounds: converted.rounds,
              turns: (converted.rounds ?? 0) > 0 ? converted.turns : Math.max(converted.turns ?? 1, 1),
              startRound: game.combat?.round,
              startTurn: game.combat?.turn
            };
          }
          const effects = await this.actor.createEmbeddedDocuments("ActiveEffect", [effectData]);
          if (effects.length > 0) {
            for (let sc of this.summonedCreatures) {
              sc.setFlag("dnd5e", "dependentOn", effects[0].uuid);
            }
          }
        }
      }
    }
    if (configSettings.autoUndoTransformations && this.transformedActorUuids && this.transformedActorUuids.length > 0) {
      let origin = getConcentrationEffect(this.actor, this.item);
      if (origin) {
        await origin.setFlag("midi-qol", "transformedActorUuids", this.transformedActorUuids);
        if (this.transformedActors) {
          for (const polyActor of this.transformedActors) {
            const polyConcentration = getConcentrationEffect(polyActor, this.item);
            if (polyConcentration) {
              await polyConcentration.setFlag("midi-qol", "transformedActorUuids", this.transformedActorUuids);
            }
          }
        }
      } else {
        const activityDuration = this.activity.duration;
        let effectData;
        const transformString = i18n("DND5E.TRANSFORM.Action.Transform") + ": ";
        let effect = this.item.actor?.effects.find((ef) => ef.name === transformString + this.item.name);
        if (effect) {
          const transformedUuids = effect.getFlag("midi-qol", "transformedActorUuids") ?? [];
          await effect.setFlag("midi-qol", "transformedActorUuids", [...transformedUuids, ...this.transformedActorUuids]);
        } else {
          effectData = {
            origin: this.item.uuid,
            //flag the effect as associated to the spell being cast
            disabled: false,
            img: this.item.img,
            name: transformString + this.item.name,
            duration: {},
            flags: {
              dae: {
                stackable: "noneName"
              },
              "midi-qol": { transformedActorUuids: this.transformedActorUuids }
            }
          };
          const inCombat2 = !!game.combat?.turns.some((combatant) => combatant.token?.id === selfTarget?.id);
          if (activityDuration?.units === "inst") {
            effectData.duration = {
              startRound: game.combat?.round,
              startTurn: game.combat?.turn,
              startTime: game.time?.worldTime
            };
          } else if (CONFIG.DND5E.permanentTimePeriods[activityDuration.units]) {
            effectData.duration = {
              startRound: game.combat?.round,
              startTurn: game.combat?.turn,
              startTime: game.time?.worldTime
            };
          } else {
            const convertedDuration = convertDuration(activityDuration, inCombat2);
            if (convertedDuration.type === "seconds") {
              effectData.duration = { seconds: Math.max(convertedDuration.seconds ?? 1, 1), startTime: game.time?.worldTime };
            } else if (convertedDuration.type === "turns") {
              effectData.duration = {
                rounds: convertedDuration.rounds,
                turns: (convertedDuration.rounds ?? 0) > 0 ? convertedDuration.turns : Math.max(convertedDuration.turns ?? 1, 1),
                startRound: game.combat?.round,
                startTurn: game.combat?.turn
              };
            }
          }
          await this.actor.createEmbeddedDocuments("ActiveEffect", [effectData]);
        }
      }
    }
    await this.callMidiHooks("midi-qol.postActiveEffects");
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("postActiveEffects"), "OnUse", "postActiveEffects");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("postActiveEffects"), "OnUse", "postActiveEffects");
    }
    if (this.aborted) return this.WorkflowState_Abort;
    await this.callMidiHooks("midi-qol.RollComplete");
    if (this.aborted) return this.WorkflowState_Abort;
    if (autoRemoveTargets !== "none") setTimeout(untargetDeadTokens, 500);
    const inCombat = this.actor && isInCombat(this.actor);
    let activeCombatants = game.combats?.combats.map((combat) => combat.combatant?.token?.id);
    const isTurn = activeCombatants?.includes(this.token?.id);
    if (inCombat && isTurn && this.activity.activation?.type === "action" && !this.AoO) {
      await setActionUsed(this.actor);
    }
    return this.WorkflowState_Cleanup;
  }
  async WorkflowState_Default(context = {}) {
    return this.WorkflowState_Suspend;
  }
  initSaveResults() {
    this.saves = /* @__PURE__ */ new Set();
    this.criticalSaves = /* @__PURE__ */ new Set();
    this.fumbleSaves = /* @__PURE__ */ new Set();
    this.failedSaves = this.activity.hasAttack ? new Set(this.hitTargets) : new Set(this.targets);
    this.advantageSaves = /* @__PURE__ */ new Set();
    this.disadvantageSaves = /* @__PURE__ */ new Set();
    this.saveDisplayData = [];
    this.saveResults = [];
  }
  async checkAttackAdvantage() {
    this.attackRollModifierTracker.reset();
    if (this.workflowOptions?.advantage) {
      this.attackRollModifierTracker.advantage.add("workflowOptions", i18n("midi-qol.WorkflowOptions"));
    }
    if (this.workflowOptions?.disadvantage) {
      this.attackRollModifierTracker.disadvantage.add("workflowOptions", i18n("midi-qol.WorkflowOptions"));
    }
    if (this.longRangeAttack) {
      this.attackRollModifierTracker.disadvantage.add("range", "Long Range");
    }
    await this.checkFlankingAdvantage();
    if (this.flankingAdvantage) this.attackRollModifierTracker.advantage.add("flanking", i18n("midi-qol.Flanking"));
    if (!this.actor) return;
    const actType = this.activity.actionType || "none";
    const firstTarget = this.targets.first();
    let attackAbility;
    if (["mwak", "rwak"].includes(actType)) {
      attackAbility = this.item?.abilityMod ?? "";
      if (!attackAbility) attackAbility = this.activity.item?.system.properties?.has("fin") ? "dex" : "str";
    }
    await computeAttackAdvantage(
      this.actor,
      actType,
      { workflow: this, item: this.item },
      attackAbility,
      this.attackRollModifierTracker
    );
    let conditionData;
    if (checkRule("invisAdvantage") && checkRule("invisAdvantage") !== "none" && firstTarget) {
      const invisibleToken = this.token ? hasCondition(this.token.actor, "invisible") : false;
      const invisibleTarget = hasCondition(firstTarget.actor, "invisible");
      const isRAW2014 = checkRule("invisAdvantage") === "RAW2014";
      const tokenCanPerceive = isRAW2014 ? this.tokenCanSense?.has(firstTarget) : this.tokenCanSee?.has(firstTarget);
      const targetCanPerceive = isRAW2014 ? this.token && this.targetsCanSense?.has(this.token) : this.token && this.targetsCanSee?.has(this.token);
      const invisAdvantage = isRAW2014 ? invisibleToken || !targetCanPerceive : !targetCanPerceive;
      if (invisAdvantage) {
        if (!targetCanPerceive) {
          this.attackRollModifierTracker.advantage.add("Attacker not detected", "Attacker not detected");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.advantage.attack.invisible", { value: true, effects: ["Attacker not detected"] });
        }
        if (invisibleToken && isRAW2014) {
          this.attackRollModifierTracker.advantage.add("Attacker invisible", "Attacker invisible");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.advantage.attack.invisible", { value: true, effects: ["Invisible Attacker"] });
        }
      }
      const invisDisadvantage = isRAW2014 ? invisibleTarget || !tokenCanPerceive : !tokenCanPerceive;
      if (invisDisadvantage) {
        log(`Disadvantage given to ${this.actor.name} due to invisible target`, invisibleTarget, tokenCanPerceive);
        if (invisibleTarget && isRAW2014) {
          this.attackRollModifierTracker.disadvantage.add("invisible foe", "Invisible foe");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.disadvantage.attack.invisible", { value: true, effects: ["Invisible Defender"] });
        }
        if (!tokenCanPerceive) {
          this.attackRollModifierTracker.disadvantage.add("Defender not detected", "Defender not detected");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.disadvantage.attack.invisible", { value: true, effects: ["Defender not detected"] });
        }
      }
    }
    if (checkRule("hiddenAdvantage") && checkRule("hiddenAdvantage") !== "none" && firstTarget) {
      if (checkRule("hiddenAdvantage") === "perceptive" && installedModules.get("perceptive")) {
        const perceptiveApi = game.modules.get("perceptive")?.api;
        const tokenSpotted = await perceptiveApi?.isSpottedby(this.token, firstTarget, { LOS: false, Range: true, Effects: true, Hidden: false, canbeSpotted: true }) ?? true;
        const targetSpotted = await perceptiveApi?.isSpottedby(firstTarget, this.token, { LOS: false, Range: true, Effects: true, Hidden: false, canbeSpotted: true }) ?? true;
        if (!tokenSpotted) {
          this.attackRollModifierTracker.advantage.add("Attacker hidden", "Attacker hidden");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.advantage.attack.hidden", { value: true, effects: ["Hidden Attacker"] });
        }
        if (!targetSpotted) {
          this.attackRollModifierTracker.disadvantage.add("Hidden foe", "Hidden foe");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.disadvantage.attack.hidden", { value: true, effects: ["Hidden Defender"] });
        }
      }
      if (checkRule("hiddenAdvantage") === "effect") {
        const hiddenToken = this.token ? hasCondition(this.token.actor, "hidden") : false;
        const hiddenTarget = hasCondition(firstTarget.actor, "hidden");
        if (hiddenToken) {
          this.attackRollModifierTracker.advantage.add("Attacker hidden", "Attacker hidden");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.advantage.attack.hidden", { value: true, effects: ["Hidden Attacker"] });
        }
        if (hiddenTarget) {
          this.attackRollModifierTracker.disadvantage.add("Target hidden", "Target hidden");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.disadvantage.attack.hidden", { value: true, effects: ["Hidden Defender"] });
        }
      }
    }
    if (checkRule("nearbyFoe")) {
      conditionData ??= createConditionData({ workflow: this, target: firstTarget, actor: this.actor });
      const ignoreNearbyFoes = await evalAllConditionsAsync(this.actor, midiFlag2(`ignoreNearbyFoes`), conditionData);
      if (!ignoreNearbyFoes && (["rwak", "rsak"].includes(actType) || this.item.system.properties?.has("thr") && actType == "mwak")) {
        let nearbyFoe = false;
        if (this.token) {
          if (actType !== "mwak") {
            nearbyFoe = checkNearby(-1, getToken(this.token), configSettings.optionalRules.nearbyFoe, { includeIncapacitated: false, canSee: true });
          } else if (this.item.system.properties?.has("thr")) {
            const meleeRange = this.item.system.range.reach ?? 5;
            if (getDistance(this.token, firstTarget, { wallsBlock: false }) <= meleeRange) nearbyFoe = false;
            else nearbyFoe = checkNearby(-1, getToken(this.token), configSettings.optionalRules.nearbyFoe, { includeIncapacitated: false, canSee: true });
          }
          if (nearbyFoe) {
            if (debugEnabled > 0) warn(`checkAttackAdvantage | Ranged attack by ${this.actor.name} at disadvantage due to nearby foe`);
            this.attackRollModifierTracker.disadvantage.add("nearbyFoe", "Nearby foe");
            foundry.utils.setProperty(this.actor, "flags.midi.evaluated.disadvantage.attack.nearbyFoe", { value: true, effects: ["Nearby Foe"] });
          }
        }
      }
    }
    if (this.item.system.properties?.has("hvy")) {
      const failDisadvantageHeavy = this.actor.flags?.[MODULE_ID]?.fail?.disadvantage?.heavy;
      if (failDisadvantageHeavy) conditionData ??= createConditionData({ workflow: this, target: firstTarget, actor: this.actor });
      if (!failDisadvantageHeavy || !await evalAllConditionsAsync(this.actor, midiFlag2(`fail.disadvantage.heavy`), conditionData)) {
        if (safeGetGameSetting("dnd5e", "rulesVersion") === "modern") {
          if (this.activity.actionType === "mwak" && this.actor.system.abilities?.str.value < 13 || // @ts-expect-error no dnd5e-types
          this.activity.actionType === "rwak" && this.actor.system.abilities?.dex.value < 13) {
            this.attackRollModifierTracker.disadvantage.add("heavy weapon", "Heavy weapon");
          }
        } else if (safeGetGameSetting("dnd5e", "rulesVersion") === "legacy") {
          if (["tiny", "sm"].includes(this.actor.system.traits?.size)) {
            this.attackRollModifierTracker.disadvantage.add("small", "Small creature");
          }
        }
      }
    }
    if (firstTarget && safeGetGameSetting("dnd5e", "rulesVersion") === "modern" && hasCondition(this.actor, "frightened")) {
      for (const ef of this.actor.allApplicableEffects()) {
        if (ef.disabled || !ef.statuses.has("frightened")) continue;
        const origin = ef.origin ? fromUuidSync(ef.origin) : void 0;
        let sourceActor;
        if (origin instanceof Actor) {
          sourceActor = origin;
        } else if (origin) {
          let doc = origin;
          while (doc && !(doc instanceof Actor)) doc = doc.parent;
          if (doc instanceof Actor) sourceActor = doc;
        }
        if (sourceActor && firstTarget.actor?.uuid === sourceActor.uuid) {
          this.attackRollModifierTracker.disadvantage.add("frightened", "Frightened");
          break;
        }
      }
    }
    await this.checkTargetAdvantage();
  }
  async processCriticalFlags() {
    if (!this.actor) return;
    const attackType = this.activity?.actionType;
    const trackerHadCritical = this.attackRollModifierTracker.critical.isForced;
    const trackerHadNoCritical = this.attackRollModifierTracker.critical.isSuppressed;
    if (this.hitTargets.size >= 1) {
      for (const target of this.hitTargets) {
        if (!target?.actor) continue;
        const targetName = target?.name ?? target?.actor?.name ?? "";
        const grants = target?.actor?.flags[MODULE_ID]?.grants?.critical ?? {};
        const fails = target?.actor?.flags[MODULE_ID]?.fail?.critical ?? {};
        if (Number.isNumeric(grants.range) && getDistance(target, this.token, { wallsBlock: false }) <= Number(grants.range)) {
          this.attackRollModifierTracker.critical.force("grants.critical.range", `${getFlagDisplayName("grants.critical.range")} (${targetName})`);
        }
        if (grants || fails) {
          const conditionData = createConditionData({ workflow: this, target, actor: this.actor });
          if (await evalAllConditionsAsync(target.actor, midiFlag2(`grants.critical.all`), conditionData)) {
            this.attackRollModifierTracker.critical.force("grants.critical.all", `${getFlagDisplayName("grants.critical.all")} (${targetName})`);
          }
          if (attackType && await evalAllConditionsAsync(target.actor, midiFlag2(`grants.critical.${attackType}`), conditionData)) {
            this.attackRollModifierTracker.critical.force(`grants.critical.${attackType}`, `${getFlagDisplayName(`grants.critical.${attackType}`)} (${targetName})`);
          }
          if (await evalAllConditionsAsync(target.actor, midiFlag2(`fail.critical.all`), conditionData) || attackType && await evalAllConditionsAsync(target.actor, midiFlag2(`fail.critical.${attackType}`), conditionData)) {
            console.warn(`midi-qol | processCriticalFlags | ${target.actor.name} flags.${MODULE_ID}.fail.critical is deprecated use flags.${MODULE_ID}.grants.noCritical.all/.attackType instead`);
            this.attackRollModifierTracker.critical.suppress("fail.critical", `${getFlagDisplayName("fail.critical")} (${targetName})`);
          }
        }
        if (this.attackRollModifierTracker.critical.isForced) break;
      }
      let allTargetsGrantNoCrit = true;
      const noCritTargets = [];
      let noCritFlag = "grants.noCritical.all";
      for (const target of this.hitTargets) {
        if (!target?.actor) {
          allTargetsGrantNoCrit = false;
          break;
        }
        const targetName = target?.name ?? target?.actor?.name ?? "";
        const conditionData = createConditionData({ workflow: this, target, actor: this.actor });
        const hasNoCritAll = await evalAllConditionsAsync(target.actor, midiFlag2(`grants.noCritical.all`), conditionData);
        const hasNoCritType = attackType && await evalAllConditionsAsync(target.actor, midiFlag2(`grants.noCritical.${attackType}`), conditionData);
        if (!hasNoCritAll && !hasNoCritType) {
          allTargetsGrantNoCrit = false;
          break;
        }
        if (hasNoCritType) noCritFlag = `grants.noCritical.${attackType}`;
        noCritTargets.push(targetName);
      }
      if (allTargetsGrantNoCrit && this.hitTargets.size > 0) {
        this.attackRollModifierTracker.critical.suppress(noCritFlag, `${getFlagDisplayName(noCritFlag)} (${noCritTargets.join(", ")})`);
      }
    }
    this.persistFields({ isCritical: this.isCritical });
    const criticalChanged = this.attackRollModifierTracker.critical.isForced !== trackerHadCritical;
    const noCriticalChanged = this.attackRollModifierTracker.critical.isSuppressed !== trackerHadNoCritical;
    if (this.attackRoll && (criticalChanged || noCriticalChanged)) {
      addAttribution(this.attackRoll, this.attackAttribution);
      this.attackRollHTML = await midiRenderAttackRoll(this.attackRoll);
    }
  }
  async checkFlankingAdvantage() {
    if (!canvas) {
      console.warn("midi-qol | checkFlankingAdvantage | abandoned - no canvas defined");
      return false;
    }
    this.flankingAdvantage = false;
    if (this.item && !["mwak", "msak"].includes(this.activity.actionType ?? "")) return false;
    const token = fromUuidSync(this.tokenUuid)?.object;
    const target = this.targets.first();
    const flankingMode = checkRule("checkFlanking");
    const isFlankedMode = ["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(flankingMode);
    const flankingBonusSetting = configSettings.optionalRules.flankingBonus ?? "advantage";
    if (isFlankedMode) {
      if (flankingBonusSetting === "advantage") {
        await markFlanking(token, target);
      }
      return false;
    }
    if (flankingBonusSetting === "advantage") {
      const needsFlanking = await markFlanking(token, target);
      if (needsFlanking && this.actor) {
        foundry.utils.setProperty(this.actor, "flags.midi.evaluated.advantage.attack.flanking", { value: true, effects: ["Flanking"] });
      }
      if (flankingMode === "advonly") {
        this.flankingAdvantage = needsFlanking;
      }
      return needsFlanking;
    }
    return false;
  }
  async recordFlankingBonuses() {
    if (!["mwak", "msak"].includes(this.activity?.actionType ?? "")) return;
    const flankingBonusSetting = configSettings.optionalRules.flankingBonus ?? "advantage";
    if (flankingBonusSetting === "advantage") return;
    const bonusValue = Number(flankingBonusSetting) || 0;
    if (!bonusValue) return;
    const flankingMode = checkRule("checkFlanking");
    const isFlankingMode = ["advonly", "ceonly", "midiFlanking"].includes(flankingMode);
    const isFlankedMode = ["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(flankingMode);
    if (!isFlankingMode && !isFlankedMode) return;
    if (isFlankingMode && !this.token) return;
    const allDescriptors = this.targetDescriptors;
    if (!allDescriptors) return;
    let modified = false;
    for (const t of this.targets) {
      const hasBonus = isFlankingMode ? await computeFlankingStatus(this.token, t) : await computeFlankedStatus(t);
      if (hasBonus) {
        const targetDescriptor = getTargetDescriptor(t.actor?.uuid ?? "", allDescriptors);
        if (targetDescriptor) {
          targetDescriptor.flankingBonus = bonusValue;
          modified = true;
        }
      }
    }
    if (modified) {
      this.targetDescriptors = allDescriptors;
    }
  }
  async checkTargetAdvantage() {
    if (!this.item) return;
    if (!this.targets?.size) return;
    const actionType = this.activity.actionType ?? "";
    const firstTargetDocument = getTokenDocument(this.targets.first());
    const firstTarget = getToken(firstTargetDocument);
    if (!firstTargetDocument || !firstTarget) return;
    if (checkRule("nearbyAllyRanged") > 0 && ["rwak", "rsak"].includes(actionType)) {
      if ((firstTargetDocument.width ?? 1) * (firstTargetDocument.height ?? 1) < Number(checkRule("nearbyAllyRanged"))) {
        const nearbyAlly = checkNearby(-1, firstTarget, canvas.dimensions?.distance ?? 5);
        if (nearbyAlly) {
          if (debugEnabled > 0) warn("checkTargetAdvantage | ranged attack with disadvantage because target is near a friend");
          this.attackRollModifierTracker.disadvantage.add("nearbyAlly", "Nearby Ally");
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.disadvantage.attack.nearbyAlly", { value: true, effects: ["Nearby Ally"] });
        }
      }
    }
  }
  async triggerTargetMacros(triggerList, targets = this.targets, options = {}) {
    let results = {};
    for (let target of targets) {
      results[target.document.uuid] = [];
      let result = results[target.document.uuid];
      const actorOnUseMacros = target.actor?.flags?.[MODULE_ID]?.onUseMacroParts ?? new OnUseMacros();
      const wasAttacked = !!this.activity.attack;
      const wasHit = (this.activity ? wasAttacked : true) && (this.hitTargets?.has(target) || this.hitTargetsEC?.has(target));
      const wasMissed = (this.activity ? wasAttacked : true) && !this.hitTargets?.has(target) && !this.hitTargetsEC?.has(target);
      const wasDamaged = this.damageList && (this.hitTargets.has(target) || this.hitTargetsEC.has(target)) && this.damageList.some((dl) => dl.targetUuid === target.document.uuid && (dl.hpDamage > 0 || dl.tempDamage > 0));
      if (this.targets.has(target) && triggerList.includes("isTargeted")) {
        if (triggerList.includes("isTargeted")) {
          result.push(
            ...await this.callMacros(
              this.item,
              actorOnUseMacros?.getMacros("isTargeted"),
              "TargetOnUse",
              "isTargeted",
              { actor: target.actor, token: target }
            )
          );
        }
      }
      if (wasAttacked && triggerList.includes("isPreAttacked")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isPreAttacked"),
            "TargetOnUse",
            "isPreAttacked",
            { actor: target.actor, token: target }
          )
        );
      }
      if (wasAttacked && triggerList.includes("isAttacked")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isAttacked"),
            "TargetOnUse",
            "isAttacked",
            { actor: target.actor, token: target }
          )
        );
      }
      if (triggerList.includes("postTargetEffectApplication")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("postTargetEffectApplication"),
            "TargetOnUse",
            "postTargetEffectApplication",
            { actor: target.actor, token: target }
          )
        );
      }
      if (wasHit && triggerList.includes("isHit")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isHit"),
            "TargetOnUse",
            "isHit",
            { actor: target.actor, token: target }
          )
        );
      }
      if (wasMissed && triggerList.includes("isMissed")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isMissed"),
            "TargetOnUse",
            "isMissed",
            { actor: target.actor, token: target }
          )
        );
      }
      if (triggerList.includes("preTargetDamageApplication")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("preTargetDamageApplication"),
            "TargetOnUse",
            "preTargetDamageApplication",
            { actor: target.actor, token: target }
          )
        );
      }
      if (this.activityHasSave && triggerList.includes("preTargetSave")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("preTargetSave"),
            "TargetOnUse",
            "preTargetSave",
            { actor: target.actor, token: target }
          )
        );
      }
      if (this.activityHasSave && triggerList.includes("isAboutToSave")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isAboutToSave"),
            "TargetOnUse",
            "isAboutToSave",
            { actor: target.actor, token: target }
          )
        );
      }
      if (target.actor?.uuid !== this.actor.uuid && triggerList.includes("1Reaction")) {
      }
      if (this.activityHasSave && triggerList.includes("isSaveSuccess") && (this.saves.has(target) || options.saved === true)) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isSaveSuccess"),
            "TargetOnUse",
            "isSaveSuccess",
            { actor: target.actor, token: target }
          )
        );
      }
      if (this.activityHasSave && triggerList.includes("isSaveFailure") && (!this.saves.has(target) || options.saved === false)) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isSaveFailure"),
            "TargetOnUse",
            "isSaveFailure",
            { actor: target.actor, token: target }
          )
        );
      }
      if (this.activityHasSave && triggerList.includes("isSave")) {
        result.push(
          ...await this.callMacros(
            this.item,
            actorOnUseMacros?.getMacros("isSave"),
            "TargetOnUse",
            "isSave",
            { actor: target.actor, token: target }
          )
        );
      }
    }
    return results;
  }
  async callMidiHooks(...hookNames) {
    for (const hookName of hookNames) {
      await asyncHooksCallAll(hookName, this);
      if (this.item) await asyncHooksCallAll(`${hookName}.${this.item.uuid}`, this);
      if (this.activity) await asyncHooksCallAll(`${hookName}.${this.activity.uuid}`, this);
    }
  }
  async callCancellableHooks(hookName) {
    if (await asyncHooksCall2(hookName, this) === false) return false;
    if (this.item && await asyncHooksCall2(`${hookName}.${this.item.uuid}`, this) === false) return false;
    if (this.activity && await asyncHooksCall2(`${hookName}.${this.activity.uuid}`, this) === false) return false;
    if (await asyncHooksCall2(`${hookName}.${this.id}`, this) === false) return false;
    const premadesHook = hookName.replace("midi-qol.", "midi-qol.premades.");
    if (await asyncHooksCall2(premadesHook, this) === false) return false;
    if (this.item && await asyncHooksCall2(`${premadesHook}.${this.item.uuid}`, this) === false) return false;
    if (this.activity && await asyncHooksCall2(`${premadesHook}.${this.activity.uuid}`, this) === false) return false;
    if (await asyncHooksCall2(`${premadesHook}.${this.id}`, this) === false) return false;
    return true;
  }
  async expireTargetEffects(expireList) {
    if (debugEnabled > 0) warn(`expireTargetEffects | ${expireList}`);
    for (let target of this.targets) {
      const expiryReason = [];
      const appliedEffects = getAppliedEffects(target.actor, { includeEnchantments: true });
      if (!appliedEffects) continue;
      const expiredEffects = appliedEffects.filter((ef) => {
        let wasExpired = false;
        const specialDuration = ef.flags?.dae?.specialDuration;
        if (!specialDuration) return false;
        const wasAttacked = this.activity.attack;
        const wasHit = (this.item ? wasAttacked : true) && (this.hitTargets?.has(target) || this.hitTargetsEC?.has(target));
        const wasDamaged = this.damageList && (this.hitTargets.has(target) || this.hitTargetsEC.has(target)) && this.damageList.find((dl) => dl.targetUuid === target.document.uuid && dl.hpDamage > 0);
        const wasHealed = this.damageList && this.damageList.find((dl) => dl.targetUuid === target.document.uuid && dl.hpDamage < 0);
        if (wasAttacked && expireList.includes("isAttacked") && specialDuration.includes("isAttacked")) {
          wasExpired = true;
          expiryReason.push("isAttacked");
        }
        if (wasHit && this.isCritical && expireList.includes("isAttacked") && specialDuration.includes("isHitCritical")) {
          wasExpired = true;
          expiryReason.push("isHitCritical");
        }
        if (wasDamaged && expireList.includes("isDamaged") && !configSettings.autoApplyDamage.toLocaleLowerCase().includes("yes") && specialDuration.includes("isDamaged")) {
          wasExpired = true;
          expiryReason.push("isDamaged");
        }
        if (wasHealed && expireList.includes("isHealed") && !configSettings.autoApplyDamage.toLocaleLowerCase().includes("yes") && specialDuration.includes("isHealed")) {
          wasExpired = true;
          expiryReason.push("isHealed");
        }
        if (wasHit && expireList.includes("isHit") && specialDuration.includes("isHit")) {
          wasExpired = true;
          expiryReason.push("isHit");
        }
        if (target.actor?.uuid !== this.actor.uuid && expireList.includes("1Reaction") && specialDuration.includes("1Reaction")) {
          wasExpired = true;
          expiryReason.push("1Reaction");
        }
        if (this.rawDamageDetail) for (let dt of this.rawDamageDetail) {
          if (expireList.includes(`isDamaged`) && (wasDamaged || dt.type === "healing") && specialDuration.includes(`isDamaged.${dt.type}`)) {
            wasExpired = true;
            expiryReason.push(`isDamaged.${dt.type}`);
            break;
          }
        }
        if (!this.activity) return wasExpired;
        if ((this.activity.save || this.activity.check) && expireList.includes("isSaveSuccess") && specialDuration.includes(`isSaveSuccess`) && this.saves.has(target)) {
          wasExpired = true;
          expiryReason.push(`isSaveSuccess`);
        }
        if ((this.activity.save || this.activity.check) && expireList.includes("isSaveFailure") && specialDuration.includes(`isSaveFailure`) && !this.saves.has(target)) {
          wasExpired = true;
          expiryReason.push(`isSaveFailure`);
        }
        if ((this.activity.save || this.activity.check) && expireList.includes("isSave") && specialDuration.includes(`isSave`)) {
          wasExpired = true;
          expiryReason.push(`isSave`);
        }
        const abl = this.activity.save?.ability ?? this.activity.check?.ability;
        if ((this.activity.save || this.activity.check) && expireList.includes(`isSaveSuccess`) && specialDuration.includes(`isSaveSuccess.${abl}`) && this.saves.has(target)) {
          wasExpired = true;
          expiryReason.push(`isSaveSuccess.${abl}`);
        }
        ;
        if ((this.activity.save || this.activity.check) && expireList.includes(`isSaveFailure`) && specialDuration.includes(`isSaveFailure.${abl}`) && !this.saves.has(target)) {
          wasExpired = true;
          expiryReason.push(`isSaveFailure.${abl}`);
        }
        ;
        if ((this.activity.save || this.activity.check) && expireList.includes(`isSave`) && specialDuration.includes(`isSave.${abl}`)) {
          wasExpired = true;
          expiryReason.push(`isSave.${abl}`);
        }
        ;
        return wasExpired;
      }).map((ef) => ef.uuid);
      if (expiredEffects.length > 0) {
        await timedAwaitExecuteAsGM("removeEffectUuids", {
          actorUuid: target.actor?.uuid,
          effects: expiredEffects,
          options: { "expiry-reason": `midi-qol:${expiryReason}` }
        });
      }
    }
  }
  getDamageBonusMacros() {
    const actorMacros = this.actor.getFlag("dnd5e", "DamageBonusMacro");
    const itemMacros = this.onUseMacros?.getMacros("damageBonus");
    if (!itemMacros?.length) return actorMacros;
    if (!actorMacros?.length) return itemMacros;
    return `${actorMacros},${itemMacros}`;
  }
  async rollBonusDamage(damageBonusMacro) {
    const extraDamages = (await this.callMacros(this.item, damageBonusMacro, "DamageBonus", "DamageBonus"))?.deepFlatten();
    if (!extraDamages) return;
    let rolls = [];
    try {
      for (let damageEntry of extraDamages) {
        if (!damageEntry || typeof damageEntry !== "object") continue;
        if (damageEntry instanceof Roll) {
          rolls.push(damageEntry);
        } else {
          let { damageRoll, damageType, flavor } = damageEntry;
          if (!damageRoll) continue;
          damageType = getDamageType(damageType);
          if (!damageType && flavor && getDamageType(flavor)) damageType = getDamageType(flavor);
          if (!damageType && this.damageRolls) damageType = this.damageRolls[0].options?.type;
          if (!damageType) damageType = MidiQOL.MQdefaultDamageType;
          const rollOptions = {
            type: damageType,
            flavor: flavor ?? damageType
          };
          rolls.push(await new CONFIG.Dice.DamageRoll(damageRoll, this.item.getRollData() ?? this.actor.getRollData(), rollOptions).evaluate());
        }
      }
      await this.setBonusDamageRolls(rolls);
    } catch (err) {
      const message = `midi-qol | rollBonusDamage | error in evaluating${extraDamages} in bonus damage`;
      TroubleShooter.recordError(err, message);
      console.warn(message, err);
      this.bonusDamageRolls = void 0;
      this.rawBonusDamageDetail = [];
    }
    if (this.bonusDamageRolls && this.workflowOptions?.damageRollDSN !== false) {
      await displayDSNForRoll(this.bonusDamageRolls, "damageRoll");
    }
    return;
  }
  // TODO: Kill?
  // macroDataToObject(macroData: any): any {
  //   const data = macroData
  //   for (let documentsName of ["targets", "failedSaves", "criticalSaves", "fumbleSaves", "saves", "superSavers", "semiSuperSavers"]) {
  //     delete data[documentsName];
  //   }
  //   data.actorUuid = data.actor?.uuid;
  //   delete data.actor;
  //   data.itemUuid = data.item?.uuid;
  //   delete data.item;
  //   delete data.workflow;
  //   return data;
  // }
  getSafeMacroData(item, options = {}) {
    const niceData = this.getMacroData(item, options);
    let naughtyFields = ["actor", "workflow", "targets", "failedSaves", "criticalSaves", "fumbleSaves", "hitTargets", "hitTargetsEC", "saves", "superSavers", "semiSuperSavers"];
    for (let field of naughtyFields) delete niceData[field];
    return niceData;
  }
  getMacroData(item, options = {}) {
    if (!item) item = this.item;
    let targets = [];
    let targetUuids = [];
    let failedSaves = [];
    let criticalSaves = [];
    let criticalSaveUuids = [];
    let fumbleSaves = [];
    let fumbleSaveUuids = [];
    let failedSaveUuids = [];
    let hitTargets = [];
    let hitTargetsEC = [];
    let hitTargetUuidsEC = [];
    let hitTargetUuids = [];
    let saves = [];
    let saveUuids = [];
    let superSavers = [];
    let superSaverUuids = [];
    let semiSuperSavers = [];
    let semiSuperSaverUuids = [];
    for (let target of this.targets) {
      targets.push(target.document);
      targetUuids.push(target.document.uuid);
    }
    for (let save of this.saves) {
      saves.push(save.document);
      saveUuids.push(save.document.uuid);
    }
    for (let hit of this.hitTargets) {
      hitTargets.push(hit.document);
      hitTargetUuids.push(hit.document.uuid);
    }
    for (let hit of this.hitTargetsEC) {
      hitTargetsEC.push(hit.document);
      hitTargetUuidsEC.push(hit.document.uuid);
    }
    for (let failed of this.failedSaves) {
      failedSaves.push(failed.document);
      failedSaveUuids.push(failed.document.uuid);
    }
    for (let critical of this.criticalSaves) {
      criticalSaves.push(critical.document);
      criticalSaveUuids.push(critical.document.uuid);
    }
    for (let fumble of this.fumbleSaves) {
      fumbleSaves.push(fumble.document);
      fumbleSaveUuids.push(fumble.document.uuid);
    }
    for (let save of this.superSavers) {
      superSavers.push(save.document);
      superSaverUuids.push(save.document.uuid);
    }
    ;
    for (let save of this.semiSuperSavers) {
      semiSuperSavers.push(save.document);
      semiSuperSaverUuids.push(save.document.uuid);
    }
    ;
    const itemData = item?.toObject(false) ?? {};
    itemData.uuid = item?.uuid;
    Object.defineProperty(itemData, "actionType", { get() {
      return item?.actionType;
    } });
    return {
      actor: this.actor,
      actorData: this.actor.toObject(false),
      actorUuid: this.actor.uuid,
      advantage: this.advantage,
      attackD20: this.diceRoll,
      attackRoll: this.attackRoll,
      attackTotal: this.attackTotal,
      bonusDamageDetail: this.rawBonusDamageDetail,
      bonusDamageHTML: this.bonusDamageHTML,
      bonusDamageRolls: this.bonusDamageRolls,
      bonusDamageRoll: this.bonusDamageRoll,
      bonusDamageTotal: this.bonusDamageTotal,
      criticalSaves,
      criticalSaveUuids,
      damageDetail: this.rawDamageDetail,
      damageList: this.damageList,
      damageRoll: this.damageRoll,
      damageRolls: this.damageRolls,
      damageTotal: this.damageTotal,
      diceRoll: this.diceRoll,
      disadvantage: this.disadvantage,
      event: this.rollOptions.event,
      failedSaves,
      failedSaveUuids,
      fumbleSaves,
      fumbleSaveUuids,
      hitTargets,
      hitTargetsEC,
      hitTargetUuids,
      hitTargetUuidsEC,
      id: this.id,
      isCritical: this.rollOptions.isCritical || this.isCritical || this.workflowOptions.isCritical,
      isFumble: this.isFumble,
      item: itemData,
      itemCardUuid: this.itemCardUuid,
      itemData,
      itemUuid: item?.uuid,
      otherDamageDetail: this.rawOtherDamageDetail,
      otherDamageList: this.otherDamageList,
      otherDamageTotal: this.otherDamageTotal,
      rollData: (item ?? this.actor)?.getRollData() ?? {},
      rollOptions: this.rollOptions,
      saves,
      saveUuids,
      semiSuperSavers,
      semiSuperSaverUuids,
      castLevel: this.spellLevel,
      spellLevel: this.spellLevel,
      superSavers,
      superSaverUuids,
      targets,
      targetUuids,
      templateId: this.templateId,
      // deprecated
      templateUuid: this.templateUuid,
      // first template UUID (backward compat)
      templateUuids: this.templateUuids,
      // all template UUIDs
      tokenUuid: this.tokenUuid,
      utilityRoll: this.utilityRoll,
      utilityRolls: this.utilityRolls,
      uuid: this.itemUuid,
      // deprecated
      workflowOptions: this.workflowOptions,
      castData: this.castData,
      workflow: this,
      workflowId: this.id
    };
  }
  async callMacros(item, macros, tag, macroPass, options = {}) {
    if (!macros || macros?.length === 0) return [];
    const macroNames = macros.split(",").map((s) => s.trim());
    let values = [];
    const macroData = this.getMacroData(item ?? this.item);
    macroData.workflow = this;
    macroData.options = options;
    macroData.tag = tag;
    macroData.macroPass = macroPass;
    this.macroPass = macroPass;
    this.tag = tag;
    if (debugEnabled > 1) {
      log("callMacros | calling", macros, "for", macroPass, "with", macroData);
    }
    for (let macro of macroNames) {
      if (macroNames.length > 0 && debugEnabled > 0) {
        warn(`callMacro | "${macro}" called for ${macroPass} ${item?.name} ${item?.uuid}`);
      }
      values.push(this.callMacro(item, macro, macroData, options).catch((err) => {
        const message = `midi-qol | called macro error in ${item?.name} ${item?.uuid} macro ${macro}`;
        console.warn(message, err);
        TroubleShooter.recordError(err, message);
        return;
      }));
      if (this.activity.otherActivity) {
        values.push(this.callMacro(item, macro, macroData, foundry.utils.mergeObject(options, { otherActivityOnly: true }, { inplace: false })).catch((err) => {
          const message = `midi-qol | called macro error in ${item?.name} ${item?.uuid} macro ${macro}`;
          console.warn(message, err);
          TroubleShooter.recordError(err, message);
          return;
        }));
      }
    }
    let results = await Promise.allSettled(values);
    if (debugEnabled === 1 && results.length) warn("callMacros | macro data ", macroData);
    results = results.map((p) => p.value);
    return results;
  }
  /**
   * Parse the macro name and resolve context from UUID if provided.
   */
  async parseCallMacroContext(macroName, item, options) {
    const [name, uuid] = macroName?.trim().split("|") ?? [];
    if (!name) return void 0;
    let macroItem = item;
    let macroActivity = this.activity;
    const actorToUse = options.actor ?? this.actor;
    if (uuid?.length > 0) {
      const macroEntity = await fromUuid(uuid);
      if (macroEntity) {
        if (macroEntity instanceof ActiveEffect) {
          if (macroEntity.parent instanceof Item) macroItem = macroEntity.parent;
        } else if (macroEntity instanceof Item) {
          macroItem = macroEntity;
        } else if (macroEntity.item) {
          macroActivity = macroEntity;
          macroItem = macroEntity.item;
        }
      }
    }
    if (options.otherActivityOnly) {
      if (!this.activity.otherActivity) return void 0;
      if (name !== "ActivityMacro" && !name.startsWith("ActivityMacro-")) return void 0;
      macroActivity = this.activity.otherActivity;
      macroItem = this.item;
    }
    return { name, uuid, macroItem, macroActivity, actorToUse };
  }
  /**
   * Resolve a function.* macro specification.
   */
  resolveFunctionMacro(name, context) {
    const func = name.replace("function.", "").trim();
    return {
      item: context.macroItem,
      macroActivity: context.macroActivity,
      macroData: {
        name: "function call",
        type: "script",
        command: `return await ${func}({ speaker, actor, token, character, item, rolledItem, macroItem, args, scope, workflow })`
      }
    };
  }
  /**
   * Resolve an ItemMacro specification, with DAE itemData fallback.
   */
  async resolveItemMacroCall(name, context) {
    const { uuid, macroItem, actorToUse } = context;
    const resolved = await resolveItemMacro(name, { actor: actorToUse, originItem: macroItem });
    if (resolved) {
      return {
        item: resolved.item,
        macroActivity: context.macroActivity,
        macroData: resolved.macroData,
        sourceItemUuid: resolved.item?.uuid
      };
    }
    if (actorToUse instanceof Actor && uuid) {
      const itemData = resolveDAEItemData(actorToUse, uuid);
      if (itemData) {
        return {
          item: itemData,
          macroActivity: context.macroActivity,
          macroData: itemData?.flags?.dae?.macro ?? itemData?.flags?.itemacro?.macro,
          // @ts-expect-error uuid may not be on data
          sourceItemUuid: itemData?.uuid
        };
      }
    }
    console.warn("midi-qol | callMacro | No item for", name);
    return void 0;
  }
  /**
   * Resolve an ActivityMacro specification.
   */
  async resolveActivityMacroCall(name, context) {
    const { macroItem, macroActivity } = context;
    if (name.startsWith("ActivityMacro-")) {
      const activityId = name.split("-").slice(1).join("-");
      if (!macroActivity || activityId !== macroActivity?.id) return void 0;
    }
    const resolved = await resolveActivityMacro(name, {
      originItem: macroItem,
      originActivity: macroActivity
    });
    if (resolved) {
      return {
        item: resolved.item,
        macroActivity: resolved.activity,
        macro: resolved.macro,
        sourceItemUuid: resolved.item?.uuid
      };
    }
    return void 0;
  }
  /**
   * Resolve a world or compendium macro.
   */
  async resolveWorldMacro(name, context) {
    const macroName = name.startsWith("Macro.") ? name.slice(6) : name;
    let macro = game.macros?.getName(macroName);
    if (!macro) {
      const doc = await fromUuid(macroName);
      if (doc instanceof Item) {
        return {
          item: doc,
          macroActivity: context.macroActivity,
          macroData: doc.flags?.dae?.macro ?? doc.flags?.itemacro?.macro,
          sourceItemUuid: doc.uuid
        };
      } else if (doc instanceof Macro) {
        macro = doc;
      }
    }
    if (macro?.type === "chat") {
      macro.execute();
      return void 0;
    }
    return macro ? { item: context.macroItem, macroActivity: context.macroActivity, macro } : void 0;
  }
  /**
   * Create a Macro instance from macro data and execute it with the given scope.
   * @param resolved - The resolved macro specification
   * @param workflowContext - Workflow context data passed to macro as args[0] and midiData
   * @param options - Call options
   * @param rolledItem - The item that was rolled to trigger this macro
   */
  executeMacroWithScope(resolved, workflowContext, options, rolledItem) {
    const macro = this.createMacroFromResolved(resolved);
    if (!macro?.command) return void 0;
    const actorToUse = options.actor ?? this.actor;
    const scope = {
      workflow: this,
      rolledActivity: this.activity,
      macroActivity: resolved.macroActivity,
      item: rolledItem,
      rolledItem,
      macroItem: resolved.item ?? rolledItem,
      args: [workflowContext],
      options,
      actor: actorToUse,
      token: tokenForActor(actorToUse),
      midiData: workflowContext
    };
    return macro.execute(scope);
  }
  /**
   * Create a Macro document from resolved macro data.
   */
  createMacroFromResolved(resolved) {
    if (resolved.macro) return resolved.macro;
    const macroData = resolved.macroData;
    if (!macroData?.command) {
      if (debugEnabled > 0) warn(`callMacro | could not find item macro command`);
      return void 0;
    }
    const data = foundry.utils.mergeObject(
      { name: "midi generated macro", type: "script", command: "" },
      macroData
    );
    data.ownership = { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER };
    data.author = game.user?.id;
    return new CONFIG.Macro.documentClass(data);
  }
  /**
   * Call a macro by name, resolving the macro type and executing it.
   */
  async callMacro(item, macroName, macroData, options) {
    try {
      const context = await this.parseCallMacroContext(macroName, item, options);
      if (!context) return void 0;
      const { name, actorToUse } = context;
      let resolved;
      if (name.startsWith("function.")) {
        resolved = this.resolveFunctionMacro(name, context);
      } else if (name.startsWith(MQItemMacroLabel) || name.startsWith("ItemMacro")) {
        resolved = await this.resolveItemMacroCall(name, context);
      } else if (name.startsWith(MQActivityMacroLabel) || name.startsWith("ActivityMacro")) {
        resolved = await this.resolveActivityMacroCall(name, context);
      } else {
        resolved = await this.resolveWorldMacro(name, context);
        if (!resolved) {
          const message = `Could not find macro ${name}`;
          TroubleShooter.recordError(new Error(message), message);
          ui.notifications?.error(`midi-qol | ${message}`);
          return void 0;
        }
      }
      if (!resolved) return void 0;
      if (resolved.sourceItemUuid) {
        macroData.sourceItemUuid = resolved.sourceItemUuid;
      }
      macroData.speaker = this.speaker;
      macroData.actor = actorToUse;
      return this.executeMacroWithScope(resolved, macroData, options, item);
    } catch (err) {
      TroubleShooter.recordError(err, "callMacro: Error evaluating macro");
      ui.notifications?.error(`There was an error running your macro. See the console(F12) for details`);
      error2("Error evaluating macro ", err);
      return {};
    }
  }
  async removeEffectsButton() {
    if (!this.itemCardUuid) return;
    const chatMessage = this.chatCard;
    if (chatMessage?.content) {
      const buttonRe = /<button[^>]*?data-action="midiApplyEffects"[\s\S]*?<\/button>/;
      let content = chatMessage.content.replace(buttonRe, "");
      await this.performThrottledUpdate(chatMessage, { content });
    }
  }
  async displayUtilityRolls() {
    const chatMessage = this.chatCard;
    let newFlags = {};
    let content;
    if (chatMessage?.content) {
      const utilityRoll = this.utilityRoll;
      let searchRe = /<div[^>]*?class="midi-qol-utility-roll[^"]*"[\s\S]*?<div class="end-midi-qol-utility-roll">/;
      const rollString = this.activity?.roll?.name || i18n("DND5E.UTILITY.Title");
      let replaceString = `<div class="midi-qol-utility-roll"><div class="midi-roll-label">${rollString}</div>${this.utilityRollHTML}<div class="end-midi-qol-utility-roll">`;
      content = chatMessage.content.replace(searchRe, replaceString);
      if (debugEnabled > 0) warn("displayUtilityRoll |", this.utilityRoll);
      newFlags = {
        "midi-qol": {
          type: MESSAGE_TYPES.OTHER
          // roll: utilityRoll?.roll,
        }
      };
    }
    const rolls = [
      ...this.attackRoll ? [this.attackRoll] : [],
      ...this.utilityRoll ? [this.utilityRoll] : []
    ];
    await this.performThrottledUpdate(chatMessage, { content, flags: newFlags, rolls });
  }
  async displayAttackRoll(displayOptions = {}) {
    const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
    const chatMessage = this.chatCard;
    let newFlags = {};
    let content;
    if (chatMessage?.content) {
      content = chatMessage.content;
      let searchRe = /<div[^>]*?class="midi-qol-attack-roll[^"]*"[\s\S]*?<div class="end-midi-qol-attack-roll">/;
      let attackString;
      const mode = this.attackRoll?.options?.advantageMode ?? this.attackRollModifierTracker.advantageMode ?? ADV_MODE.NORMAL;
      if (mode === ADV_MODE.ADVANTAGE) {
        attackString = i18n("DND5E.Advantage");
      } else if (mode === ADV_MODE.DISADVANTAGE) {
        attackString = i18n("DND5E.Disadvantage");
      } else {
        attackString = i18n("DND5E.Attack");
      }
      if (configSettings.addFakeDice)
        attackString = i18n("DND5E.Attack");
      let attributionIcon = "";
      if (Object.keys(this.attackAttribution).length > 0) {
        const tooltipContent = await this.renderAttributionTooltip();
        if (tooltipContent) {
          attributionIcon = ` <span class="midi-attribution-info" data-tooltip-html="${foundry.utils.escapeHTML(tooltipContent)}" data-tooltip-direction="DOWN"><i class="fas fa-info-circle"></i></span>`;
        }
      }
      let replaceString = `<div class="midi-qol-attack-roll midi-qol-section-filled"><div class="midi-roll-label">${attackString}${attributionIcon}</div>${this.attackRollHTML}<div class="end-midi-qol-attack-roll">`;
      content = content.replace(searchRe, replaceString);
      if (this.attackRollCount > 1) {
        const attackButtonRe = /(<button[^>]*data-action="rollAttack"[^>]*>)(?:<span>)?(\[\d*\] )*([^<]+)(?:<\/span>)?(<\/button>)/;
        content = content.replace(attackButtonRe, `$1<span>[${this.attackRollCount}] $3</span>$4`);
        const confirmButtonRe = /<button class="midi-qol-confirm-damage-roll-complete" data-action="confirm-damage-roll-complete">(\[[\d ]*\])*([^<]+)<\/button>/;
        content = content.replace(confirmButtonRe, `<button class="midi-qol-confirm-damage-roll-complete" data-action="confirm-damage-roll-complete">[${this.attackRollCount} ${this.damageRollCount + 1}] $2</button>`);
      }
      if (this.attackRoll?.dice.length) {
        const d = this.attackRoll.dice[0];
        const isD20 = d.faces === 20;
        if (isD20) {
          this.d20AttackRoll = d.total;
        }
      }
      if (debugEnabled > 0) warn("displayAttackRoll |", this.attackRoll);
      newFlags = {
        "midi-qol": {
          GMOnlyAttackRoll: displayOptions.GMOnlyAttackRoll ?? false,
          //@ts-expect-error no dnd5e types
          isHit: configSettings.autoCheckHit === "none" ? this.attackRoll?.isSuccess ?? false : this.hitTargets.size > 0,
          isHitEC: this.hitTargetsEC.size > 0,
          // roll: this.attackRoll,
          type: MESSAGE_TYPES.ATTACK,
          d20AttackRoll: this.d20AttackRoll,
          hitTargetUuids: Array.from(this.hitTargets).map((t) => t.document.uuid),
          hitECTargetUuids: Array.from(this.hitTargetsEC).map((t) => t.document.uuid),
          isCritical: this.isCritical,
          isFumble: this.isFumble,
          targets: Array.from(this.targets).map((t) => ({ uuid: t.document.uuid, name: t.document.name }))
        }
      };
    }
    const rolls = [...this.attackRoll ? [this.attackRoll] : []];
    await this.performThrottledUpdate(chatMessage, { content, flags: newFlags, rolls });
  }
  async displayDamageRolls() {
    const chatMessage = this.chatCard;
    if (!chatMessage?.content) {
      const message = `midi-qol | displayDamageRolls | no chat message to display damage rolls`;
      console.warn(message);
      TroubleShooter.recordError(new Error(message), message);
      return;
    }
    let content = chatMessage.content;
    if (getRemoveDamageButtons(this.activity) && configSettings.confirmAttackDamage === "none" || this.workflowType === "TrapWorkflow") {
      const damageRe = /<button[^>]*?data-action="rollDamage"[\s\S]*?<\/button>/;
      const formulaRe = /<button[^>]*?data-action="rollFormula"[\s\S]*?<\/button>/;
      content = content?.replace(damageRe, "<div></div>");
      content = content?.replace(formulaRe, "");
    }
    let newFlags = {};
    if (chatMessage) {
      if (this.damageRollHTML) {
        if (!this.useOther) {
          const searchRe = /<div[^>]*?class="midi-qol-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-damage-roll">/;
          const replaceString = `<div class="midi-qol-damage-roll midi-qol-section-filled"><div class="midi-roll-label">${this.damageFlavor}</div>${this.damageRollHTML || ""}<div class="end-midi-qol-damage-roll">`;
          content = content.replace(searchRe, replaceString);
        } else {
          const otherSearchRe = /<div[^>]*?class="midi-qol-other-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-other-damage-roll">/;
          const otherReplaceString = `<div class="midi-qol-other-damage-roll midi-qol-section-filled"><div class="midi-roll-label">${this.damageFlavor}</div>${this.damageRollHTML || ""}<div class="end-midi-qol-other-damage-roll">`;
          content = content.replace(otherSearchRe, otherReplaceString);
        }
        if (this.otherDamageRollHTML) {
          const otherSearchRe = /<div[^>]*?class="midi-qol-other-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-other-damage-roll">/;
          const otherReplaceString = `<div class="midi-qol-other-damage-roll midi-qol-section-filled"><div class="midi-roll-label">${this.otherDamageFlavor}${this.otherDamageRollHTML || ""}</div><div class="end-midi-qol-other-damage-roll">`;
          content = content.replace(otherSearchRe, otherReplaceString);
        }
        if (this.bonusDamageRolls) {
          const bonusSearchRe = /<div[^>]*?class="midi-qol-bonus-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-bonus-damage-roll">/;
          const bonusReplaceString = `<div class="midi-qol-bonus-damage-roll midi-qol-section-filled"><div class="midi-roll-label">${this.bonusDamageFlavor}${this.bonusDamageHTML || ""}</div><div class="end-midi-qol-bonus-damage-roll">`;
          content = content.replace(bonusSearchRe, bonusReplaceString);
        }
      } else {
        if (this.otherDamageRollHTML) {
          const otherSearchRe = /<div[^>]*?class="midi-qol-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-damage-roll">/;
          const otherReplaceString = `<div class="midi-qol-damage-roll midi-qol-section-filled"><div class="midi-roll-label">${this.otherDamageFlavor}</div>${this.otherDamageRollHTML || ""}<div class="end-midi-qol-damage-roll">`;
          content = content.replace(otherSearchRe, otherReplaceString);
        }
        if (this.bonusDamageRolls) {
          const bonusSearchRe = /<div[^>]*?class="midi-qol-bonus-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-bonus-damage-roll">/;
          const bonusReplaceString = `<div class="midi-qol-bonus-damage-roll midi-qol-section-filled"><div class="midi-roll-label">${this.bonusDamageFlavor}</div>${this.bonusDamageHTML || ""}<div class="end-midi-qol-bonus-damage-roll">`;
          content = content.replace(bonusSearchRe, bonusReplaceString);
        }
      }
      const midiAttackTargets = Array.from(this.targets).map((token) => {
        const t = getTokenDocument(token);
        return {
          name: t?.name,
          img: t?.actor?.img,
          uuid: t?.actor?.uuid,
          //@ts-expect-error no dnd5e-types
          ac: t?.actor?.system?.attributes.ac.value
        };
      });
      newFlags = foundry.utils.mergeObject(newFlags, {
        "midi-qol": {
          type: MESSAGE_TYPES.DAMAGE,
          damageDetail: this.useOther ? void 0 : damageDetailToObject(this.rawDamageDetail),
          otherDamageDetail: this.useOther ? damageDetailToObject(this.rawDamageDetail) : damageDetailToObject(this.rawOtherDamageDetail),
          bonusDamageDetail: damageDetailToObject(this.rawBonusDamageDetail),
          sourceActorUuid: this.actor.uuid,
          damageTotal: this.useOther ? void 0 : this.damageTotal,
          ["otherDamageTotal" /* otherDamageTotal */]: this.useOther ? this.damageTotal : this.otherDamageTotal,
          ["bonusDamageTotal" /* bonusDamageTotal */]: this.bonusDamageTotal
        }
      }, { overwrite: true, inplace: false });
    }
    if (this.damageRollCount > 1) {
      const damageButtonRe = /(<button[^>]*data-action="rollDamage"[^>]*>)(?:<span>)?(\[\d*\] )*([^<]+)(?:<\/span>)?(<\/button>)/;
      content = content.replace(damageButtonRe, `$1<span>[${this.damageRollCount}] $3</span>$4`);
      const confirmButtonRe = /<button class="midi-qol-confirm-damage-roll-complete" data-action="confirm-damage-roll-complete">(\[[\d ]*\])*([^<]+)<\/button>/;
      content = content.replace(confirmButtonRe, `<button class="midi-qol-confirm-damage-roll-complete" data-action="confirm-damage-roll-complete">[${this.attackRollCount} ${Math.max(this.damageRollCount, 1)}] $2</button>`);
    } else {
      const damageButtonRe = /(<button[^>]*data-action="rollDamage"[^>]*>)(?:<span>)?(\[\d*\] )*([^<]+)(?:<\/span>)?(<\/button>)/;
      content = content.replace(damageButtonRe, `$1<span>$3</span>$4`);
    }
    return await this.performThrottledUpdate(chatMessage, { "content": content, flags: newFlags, rolls: this.chatRolls }, false);
  }
  async displayHitTargets(whisper = false) {
    this.hitDisplayData = {};
    this.needsHitTargetsDisplayed = false;
    if (this.item.type === "feat" && ["ench", "class", void 0].includes(this.activity.actionType)) return;
    for (let target of this.targets) {
      if (!target) continue;
      let img = target.document?.texture.src ?? target.actor?.img ?? "";
      if (configSettings.usePlayerPortrait && target.actor?.type === "character") {
        img = target.actor.img ?? target.document?.texture.src ?? "";
      }
      if (foundry.helpers.media.VideoHelper.hasVideoExtension(img ?? "")) {
        img = await game.video?.createThumbnail(img ?? "", { width: 100, height: 100 }) ?? "";
      }
      const tokenUuid = target.document.uuid;
      const baseAc = target.actor?.system?.attributes?.ac?.value ?? 10;
      this.hitDisplayData[tokenUuid] = {
        targetClass: target.actor?.hasPlayerOwner ? "midi-qol-player-target" : "midi-qol-npc-target",
        target,
        hitClass: "",
        acClass: target.actor?.hasPlayerOwner ? "" : "midi-qol-npc-ac",
        img,
        gmName: getTokenName(target),
        playerName: getTokenPlayerName(target) ?? "",
        uuid: tokenUuid,
        showAC: !!this.activity.attack,
        acDisplay: `${baseAc}`,
        acIcons: [],
        isHit: this.hitTargets.has(target)
      };
    }
    await this.displayHits(whisper, false);
  }
  async displayHits(whisper = false, showHits = true) {
    this.needsHitTargetsDisplayed = false;
    const templateData = {
      attackType: this.item.name ?? "",
      attackTotal: this.attackTotal,
      oneCard: true,
      collapsibleTargets: configSettings.collapsibleTargets,
      showHits,
      hits: this.hitDisplayData,
      isGM: game.user?.isGM
    };
    if (debugEnabled > 0) warn("displayHits |", templateData, whisper);
    const hitContent = await foundry.applications.handlebars.renderTemplate("modules/midi-qol/templates/hits.html", templateData) || "No Targets";
    const chatMessage = this.chatCard;
    if (chatMessage?.content) {
      let searchString = /<div[^>]*?class="midi-qol-hits-display[^"]*"[\s\S]*?<div class="end-midi-qol-hits-display">/;
      let replaceString = `<div class="midi-qol-hits-display midi-qol-section-filled">${hitContent}<div class="end-midi-qol-hits-display">`;
      let content = chatMessage.content.replace(searchString, replaceString);
      const update = {
        content,
        flags: { "midi-qol": { type: MESSAGE_TYPES.HITS } },
        // TODO: May not be required
        style: CONST.CHAT_MESSAGE_STYLES.OTHER
      };
      await this.performThrottledUpdate(chatMessage, update);
    }
  }
  computeSaveFlavors() {
    let fullDamage = [];
    let noDamage = [];
    let halfDamage = [];
    let fullDamageText = "";
    let noDamageText = "";
    let halfDamageText = "";
    if (this.activity && (this.activity.save || this.activity.check) && (this.activity.damage?.parts.length ?? 0) > 0) {
      switch (getSaveMultiplierForActivity(this.activity)) {
        case 0:
          noDamage.push(`${this.damageFlavor} &#48;`);
          break;
        case 1:
          fullDamage.push(`${this.damageFlavor} &#49;`);
          break;
        default:
          halfDamage.push(`${this.damageFlavor} &frac12;`);
      }
    }
    if ((this.otherActivity?.save || this.otherActivity?.check) && (this.otherActivity?.damage?.parts.length ?? 0) > 0) {
      switch (getSaveMultiplierForActivity(this.otherActivity)) {
        case 0:
          noDamage.push(`${this.otherDamageFlavor} &#48;`);
          break;
        case 1:
          fullDamage.push(`${this.otherDamageFlavor} &#49;`);
          break;
        default:
          halfDamage.push(`${this.otherDamageFlavor} &frac12;`);
      }
    }
    if (fullDamage.length > 0) fullDamageText = fullDamage.join(", ");
    if (noDamage.length > 0) noDamageText = noDamage.join(", ");
    if (halfDamage.length > 0) halfDamageText = halfDamage.join(", ");
    return { fullDamageText, halfDamageText, noDamageText };
  }
  //TODO refactor this with displaysaves
  async displaySaveTargets(_whisper = false) {
    this.saveTargetsDisplayed = true;
    this.saveDisplayData = [];
    let rollType = "";
    let rollAbilities = [];
    let rollSkills = [];
    let rollTools = [];
    let rollDC = this.saveActivity?.save?.dc.value ?? this.saveActivity?.check?.dc.value;
    if (this.saveActivity?.save) {
      rollType = "save";
      if (this.saveActivity.save.ability instanceof Set) {
        rollAbilities = Array.from(this.saveActivity.save.ability);
      } else {
        const ability = this.saveActivity.save.ability;
        if (ability) rollAbilities = [ability];
      }
    } else if (this.saveActivity?.check) {
      let skillOrTool = this.saveActivity.check.associated.first();
      if (!skillOrTool && this.item.type === "tool") {
        skillOrTool = this.item.system.type.baseItem;
      }
      if (!skillOrTool) {
        rollType = "check";
        const ability = this.saveActivity.check.ability;
        if (ability) rollAbilities = [ability];
      } else if (GameSystemConfig.skills[skillOrTool]) {
        rollType = "skill";
        rollSkills = [skillOrTool];
      } else {
        rollType = "tool";
        rollTools = [skillOrTool];
      }
    }
    for (let target of this.targets) {
      const targetDocument = target.document;
      let img = targetDocument.texture.src ?? target.actor?.img ?? "";
      if (configSettings.usePlayerPortrait && target.actor.type === "character") {
        img = target.actor?.img ?? targetDocument.texture.src ?? "";
      }
      if (foundry.helpers.media.VideoHelper.hasVideoExtension(img)) {
        img = await game.video?.createThumbnail(img, { width: 100, height: 100 }) ?? "";
      }
      let isPlayerOwned = !!target.actor?.hasPlayerOwner;
      this.saveDisplayData.push({
        gmName: getTokenName(target),
        playerName: getTokenPlayerName(target) ?? "",
        img,
        targetClass: isPlayerOwned ? "midi-qol-player-target" : "midi-qol-npc-target",
        target,
        saveSymbol: "",
        saveTotalClass: "",
        rollTotal: "",
        rollDetail: "",
        rollHTML: "",
        id: target.id,
        adv: void 0,
        saveClass: ""
      });
    }
    let { fullDamageText, halfDamageText, noDamageText } = this.computeSaveFlavors();
    let templateData = {
      fullDamageText,
      halfDamageText,
      noDamageText,
      hasDamageText: !!(fullDamageText || halfDamageText || noDamageText),
      saveDisplayFlavor: await this.computeSaveDisplayFlavor(rollDC, rollType, { rollAbilities, rollSkills, rollTools }),
      saves: this.saveDisplayData
      // TODO force roll damage
    };
    const chatMessage = this.chatCard;
    const saveContent = await foundry.applications.handlebars.renderTemplate("modules/midi-qol/templates/saves.html", templateData);
    let searchRe = /<div[^>]*?class="midi-qol-saves-display">[\s\S]*?<div class="end-midi-qol-saves-display">/;
    let replaceString = `<div class="midi-qol-saves-display"><div data-item-id="${this.item.id}">${saveContent}</div><div class="end-midi-qol-saves-display">`;
    let content = chatMessage.content.replace(searchRe, replaceString);
    await this.performThrottledUpdate(chatMessage, { content, flags: { "midi-qol": { type: MESSAGE_TYPES.SAVES } } });
  }
  async displaySaves(_whisper = false) {
    let { fullDamageText, halfDamageText, noDamageText } = this.computeSaveFlavors();
    let templateData = {
      fullDamageText,
      halfDamageText,
      noDamageText,
      hasDamageText: !!(fullDamageText || halfDamageText || noDamageText),
      saveDisplayFlavor: this.saveDisplayFlavor,
      saves: this.saveDisplayData
      // TODO force roll damage
    };
    let chatMessage = this.chatCard;
    if (chatMessage?.content) {
      templateData.saveDisplayFlavor = this.saveDisplayFlavor;
      const saveContent = await foundry.applications.handlebars.renderTemplate("modules/midi-qol/templates/saves.html", templateData);
      let searchRe;
      let replaceString;
      let midiTargets = Array.from(this.targets.map((t) => t.document));
      let midiTargetDescriptors;
      if (midiTargets.length) {
        const saves = this.saves?.map((t) => t.document.uuid);
        const semiSuperSavers = this.semiSuperSavers?.map((t) => t.document.uuid);
        const superSavers = this.superSavers?.map((t) => t.document.uuid);
        const targetDescriptors = this.targetDescriptors;
        midiTargetDescriptors = midiTargets.map((t) => {
          let uncannyDodge = !!(t.actor?.flags?.[MODULE_ID]?.["uncanny-dodge"] && this.activity.attack);
          let saveMults = {
            otherDamage: this.otherActivity ? getSaveMultiplierForActivity(this.otherActivity) : 1,
            defaultDamage: getSaveMultiplierForActivity(this.activity),
            bonusDamage: getSaveMultiplierForActivity(this.activity)
          };
          const targetDescriptor = getTargetDescriptor(t.actor?.uuid ?? "", targetDescriptors) ?? {};
          return foundry.utils.mergeObject({
            name: t.name,
            img: t.actor?.img,
            uuid: t.actor?.uuid ?? "",
            //@ts-expect-error no dnd5e-types
            ac: hasCoverTotal(t.actor) ? null : t?.actor?.system.attributes.ac.value,
            saved: saves?.has(t.uuid),
            semiSuperSaver: semiSuperSavers?.has(t.uuid),
            superSaver: superSavers?.has(t.uuid),
            saveMults,
            itemType: this.item.type,
            uncannyDodge
          }, targetDescriptor);
        });
      }
      if (this.workflowType !== "DamageOnlyWorkflow") {
        searchRe = /<div[^>]*?class="midi-qol-saves-display">[\s\S]*?<div class="end-midi-qol-saves-display">/;
        replaceString = `<div class="midi-qol-saves-display"><div data-item-id="${this.item.id}">${saveContent}</div><div class="end-midi-qol-saves-display">`;
        let content = chatMessage.content.replace(searchRe, replaceString);
        const update = {
          content,
          /* rolls: this.chatRolls */
          flags: {
            "midi-qol": {
              type: MESSAGE_TYPES.SAVES,
              saveUuids: Array.from(this.saves).map((t) => t.document.uuid),
              failedSaveUuids: Array.from(this.failedSaves).map((t) => t.document.uuid),
              superSaverUuids: Array.from(this.superSavers).map((t) => t.document.uuid),
              semiSuperSaverUuids: Array.from(this.semiSuperSavers).map((t) => t.document.uuid),
              criticalSaveUuids: Array.from(this.criticalSaves).map((t) => t.document.uuid),
              fumbleSaveUuids: Array.from(this.fumbleSaves).map((t) => t.document.uuid),
              advantageSaveUuids: Array.from(this.advantageSaves).map((t) => t.document.uuid),
              sourceActorUuid: this.actor.uuid
            },
            dnd5e: { targets: midiTargetDescriptors }
          },
          style: CONST.CHAT_MESSAGE_STYLES.OTHER
        };
        await this.performThrottledUpdate(chatMessage, update);
      }
    }
  }
  get chatRolls() {
    let messageRolls = [];
    if (this.attackRoll) messageRolls.push(this.attackRoll);
    if (this.utilityRoll) messageRolls.push(this.utilityRoll);
    if (this.damageRolls) messageRolls.push(...this.damageRolls);
    if (this.bonusDamageRolls) messageRolls.push(...this.bonusDamageRolls);
    if (this.otherDamageRolls) messageRolls.push(...this.otherDamageRolls);
    return messageRolls;
  }
  /**
   * Gets an auto-roll for a friendly target if applicable
   * @param saveDetails The save details object with isFriendly flag
   * @returns A promise that resolves to a roll if the target should auto-pass/fail, null otherwise
   */
  getFriendlyTargetRoll(saveDetails) {
    const BasicRoll = CONFIG.Dice.BasicRoll;
    if (!saveDetails.isFriendly) return null;
    const descriptionIncludesAutoFail = this.item.system.description?.value?.toLowerCase().includes(i18n("midi-qol.autoFailFriendly")?.toLowerCase());
    if (descriptionIncludesAutoFail || this.activity.friendlySave === "friendlyFail") {
      return new BasicRoll("-1").evaluate();
    }
    if (this.activity.friendlySave === "friendlySuccess") {
      return new BasicRoll("99").evaluate();
    }
    return null;
  }
  /**
   * Gets the portrait image for a target token
   * Handles player portrait preference and video thumbnail generation
   * @param target The target token
   * @returns Promise resolving to the image URL string
   */
  async getTargetPortraitImage(target) {
    const targetDocument = target.document;
    let img = targetDocument.texture.src ?? target.actor?.img ?? "";
    if (configSettings.usePlayerPortrait && target.actor?.type === "character") {
      img = target.actor.img ?? targetDocument.texture.src ?? "";
    }
    if (foundry.helpers.media.VideoHelper.hasVideoExtension(img)) {
      img = await game.video?.createThumbnail(img, { width: 100, height: 100 }) ?? "";
    }
    return img;
  }
  /**
   * Checks if target should auto-succeed due to Sculpt Spells or Careful Spells features
   * Sculpt Spells: Evocation wizard feature - pre-selected targets auto-succeed on saves
   * Careful Spells: Sorcerer metamagic - pre-selected targets auto-succeed on saves
   * @param target The target token to check
   * @returns true if target should auto-succeed, false otherwise
   */
  checkSculptOrCarefulSpells(target) {
    const isEvocationSpell = this.activity.item?.system.school === "evo";
    if (this.actor.flags?.[MODULE_ID]?.sculptSpells && (this.rangeTargeting || this.tempTargetConfirmation) && isEvocationSpell && this.preSelectedTargets.has(target)) {
      this.superSavers = this.superSavers.add(target);
      return true;
    }
    if (this.actor.flags?.[MODULE_ID]?.carefulSpells && (this.rangeTargeting || this.tempTargetConfirmation) && this.preSelectedTargets.has(target)) {
      return true;
    }
    return false;
  }
  /**
   * Applies concentration save bonus if applicable
   * Only applies when item has isConcentrationCheck flag and target has concentrationSaveBonus
   * @param target The target token
   * @param saveRoll The original save roll
   * @param resultDC The save DC
   * @param isCritical Whether the roll was a critical
   * @param isFumble Whether the roll was a fumble
   * @returns Updated roll info or null if no bonus applies
   */
  async applyConcentrationSaveBonus(target, saveRoll, resultDC, isCritical, isFumble) {
    if (!this.item.flags?.[MODULE_ID]?.isConcentrationCheck) return null;
    const checkBonus = target.actor?.flags?.[MODULE_ID]?.concentrationSaveBonus;
    if (!checkBonus) return null;
    const D20Roll = CONFIG.Dice.D20Roll;
    const rollBonus = await new D20Roll(`${checkBonus}`, target.actor?.getRollData()).evaluate();
    const newSaveRoll = addRollTo(saveRoll, rollBonus);
    const newSaveRollTotal = newSaveRoll.total;
    let saved = newSaveRollTotal >= resultDC;
    if (checkRule("criticalSaves")) {
      saved = (isCritical || newSaveRollTotal >= resultDC) && !isFumble;
    }
    return { saveRoll: newSaveRoll, saveRollTotal: newSaveRollTotal, saved };
  }
  /**
   * Builds and stores save display entry for a target
   * Handles advantage/disadvantage display, cover bonus, and roll rendering
   * @param target The target token
   * @param saveRoll The save roll result
   * @param saved Whether the target saved
   * @param rollDC The save DC (or undefined if no DC)
   * @param coverSaveBonus Computed cover bonus (for label when no cover status); total cover displays as ∞
   */
  async buildSaveDisplayEntry(target, saveRoll, saved, rollDC, coverSaveBonus = 0) {
    const saveRollTotal = coverSaveBonus >= TOTAL_COVER ? Infinity : saveRoll.total ?? 0;
    let adv = "";
    if (configSettings.displaySaveAdvantage) {
      adv = this.advantageSaves.has(target) ? `(${i18n("DND5E.Advantage")})` : "";
      if (this.disadvantageSaves?.has(target)) adv = `(${i18n("DND5E.Disadvantage")})`;
    }
    const img = await this.getTargetPortraitImage(target);
    const isPlayerOwned = target.actor?.hasPlayerOwner ?? false;
    let rollHTML = await midiRenderRoll(saveRoll);
    const midiChosenId = saveRoll.options?.midiChosenId;
    if (midiChosenId) {
      let choiceTitle = "";
      if (GameSystemConfig.abilities[midiChosenId]) {
        const abilityLabel = GameSystemConfig.abilities[midiChosenId].label ?? "";
        choiceTitle = i18nFormat2("DND5E.SavePromptTitle", { ability: abilityLabel });
      } else if (GameSystemConfig.skills[midiChosenId]) {
        const skillLabel = GameSystemConfig.skills[midiChosenId].label ?? "";
        choiceTitle = i18nFormat2("DND5E.SkillPromptTitle", { skill: skillLabel, ability: "" });
      } else {
        const toolLabel = getToolLabel(midiChosenId);
        if (toolLabel && toolLabel !== midiChosenId) choiceTitle = i18nFormat2("DND5E.ToolPromptTitle", { tool: toolLabel });
      }
      if (choiceTitle) rollHTML = `<div style="font-weight:bold; text-align:center;">${choiceTitle}</div>${rollHTML}`;
    }
    const targetDocUuid = getTokenDocument(target)?.uuid ?? "none";
    const saveDetails = this.targetSaveDetails?.[targetDocUuid];
    if (saveDetails) saveDetails.saveRoll = saveRoll;
    let chosenTracker;
    if (midiChosenId && saveDetails?.advantageByChoice) {
      const chosenData = saveDetails.advantageByChoice[midiChosenId];
      if (chosenData?.tracker) {
        if (chosenData.tracker instanceof RollModifierTracker) {
          chosenTracker = chosenData.tracker;
        } else {
          chosenTracker = new RollModifierTracker();
          chosenTracker.restore(chosenData.tracker);
          chosenData.tracker = chosenTracker;
        }
      }
    }
    const advMode = chosenTracker ?? saveDetails?.modifierTracker;
    const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
    const mode = saveRoll?.options?.advantageMode ?? advMode?.advantageMode ?? ADV_MODE.NORMAL;
    const title = getAdvantageModeTitle(mode);
    const attributionTooltip = await renderAttributionTooltip(advMode?.attribution ?? {}, title);
    const statusSaveCover = target.actor?.system?.attributes?.ac?.cover ?? 0;
    const totalSaveCover = statusSaveCover + coverSaveBonus;
    let coverLabel;
    if (totalSaveCover >= TOTAL_COVER) coverLabel = "systems/dnd5e/icons/svg/statuses/cover-total.svg";
    else if (totalSaveCover >= THREE_QUARTERS_COVER) coverLabel = "systems/dnd5e/icons/svg/statuses/cover-three-quarters.svg";
    else if (totalSaveCover >= HALF_COVER) coverLabel = "systems/dnd5e/icons/svg/statuses/cover-half.svg";
    let saveTooltip;
    if (rollDC !== void 0) {
      if (!isFinite(saveRollTotal)) {
        saveTooltip = `\u221E vs DC ${rollDC}`;
      } else {
        const resultString = saveRoll.result;
        if (resultString && resultString !== String(saveRollTotal)) {
          saveTooltip = `${saveRollTotal} = ${resultString} vs DC ${rollDC}`;
        } else {
          saveTooltip = `${saveRollTotal} vs DC ${rollDC}`;
        }
      }
    }
    this.saveDisplayData?.push({
      gmName: getTokenName(target),
      playerName: getTokenPlayerName(target) ?? "",
      img,
      targetClass: isPlayerOwned ? "midi-qol-player-target" : "midi-qol-npc-target",
      target,
      saveSymbol: rollDC === null ? "" : (target.actor.hasPlayerOwner ? "" : "midi-qol-npc-save-symbol") + " midi-qol-save-symbol " + (saved ? "fa-check" : "fa-xmark"),
      saveTotalClass: target.actor?.hasPlayerOwner ? "" : "midi-qol-npc-save-total",
      saveToolTipClass: target.actor?.hasPlayerOwner ? "" : "midi-qol-npc-save-tooltip",
      rollTotal: isFinite(saveRollTotal) ? String(saveRollTotal) : "\u221E",
      rollDetail: "",
      rollHTML,
      id: target.id,
      modifierTracker: saveDetails?.modifierTracker,
      // Store per-target modifierTracker for attribution
      attributionTooltip: foundry.utils.escapeHTML(attributionTooltip),
      // Escaped for data-tooltip-html, always shown
      hasAttribution: Object.keys(advMode?.attribution ?? {}).length > 0,
      coverLabel,
      saveTooltip,
      saveClass: rollDC === null ? "" : saved ? "success" : "failure"
    });
  }
  /**
   * Determines roll settings for a target's save roll
   * @param target The target token
   * @param flagRollType The roll type for module settings (save/check/skill/tool)
   * @param simulate Whether this is a simulation (disables prompts and modules)
   * @returns Object with player, roll settings, and module flags
   */
  determineTargetRollSettings(target, flagRollType, simulate) {
    let player = playerFor(target);
    if (!player || !player.active) player = preferredActiveGM();
    let promptPlayer = !player?.isGM && !["none", "noneDialogPublic", "noneDialogPrivate", "noneDialogSelf", "noneDialogBlind"].includes(configSettings.playerRollSaves);
    let showRollDialog = !player?.isGM && ["noneDialogPublic", "noneDialogPrivate", "noneDialogSelf", "noneDialogBlind"].includes(configSettings.playerRollSaves);
    if (simulate) promptPlayer = false;
    let GMprompt;
    let gmMonksTB;
    let gmFlashTB;
    let gmRER;
    const playerChat = !player?.isGM && ["chat"].includes(configSettings.playerRollSaves);
    if (player?.isGM) {
      const targetDocument = getTokenDocument(target);
      const monksTBSetting = targetDocument?.isLinked ? configSettings.rollNPCLinkedSaves === "mtb" : configSettings.rollNPCSaves === "mtb";
      const flashTBSetting = targetDocument?.isLinked ? configSettings.rollNPCLinkedSaves === "ftb" : configSettings.rollNPCSaves === "ftb";
      const epicRollsSetting = targetDocument?.isLinked ? configSettings.rollNPCLinkedSaves === "rer" : configSettings.rollNPCSaves === "rer";
      gmMonksTB = flagRollType !== "tool" && installedModules.get("monks-tokenbar") && monksTBSetting;
      gmFlashTB = installedModules.get("flash-rolls-5e") && flashTBSetting;
      gmRER = installedModules.get("epic-rolls-5e") && epicRollsSetting;
      GMprompt = targetDocument?.isLinked ? configSettings.rollNPCLinkedSaves : configSettings.rollNPCSaves;
      promptPlayer = !["auto", "autoDialog"].includes(GMprompt);
      showRollDialog = GMprompt === "autoDialog";
      if (simulate) {
        gmMonksTB = false;
        gmFlashTB = false;
        GMprompt = false;
        gmRER = false;
        promptPlayer = false;
        showRollDialog = false;
      }
    }
    return { player: player ?? void 0, promptPlayer, showRollDialog, GMprompt, gmMonksTB, gmFlashTB, gmRER, playerChat };
  }
  /**
   * Calculates the cover save bonus for a DEX save target.
   * dnd5e already bakes the status cover bonus into the DEX save via prepareAbilities(),
   * so this returns only the delta above status cover from module-computed cover.
   * @param target The target token
   * @param template The measured template (for area effects)
   * @param rollAbility The ability being used for the save
   * @returns The extra cover bonus to add to the save (above what dnd5e already includes)
   */
  async calculateCoverSaveBonus(target, template, rollAbility2) {
    if (!this.item || !this.activityHasSave || rollAbility2 !== "dex") {
      return 0;
    }
    const targetDocument = target.document;
    const targetActor = target.actor;
    let statusCoverBonus = targetActor?.system?.attributes?.ac?.cover ?? 0;
    if (hasCoverTotal(targetActor)) statusCoverBonus = TOTAL_COVER;
    let moduleCoverBonus = 0;
    let position;
    let originElevation = this.token?.document?.elevation ?? 0;
    if (this.workflowOptions.coverOrigin) {
      position = { x: this.workflowOptions.coverOrigin.x, y: this.workflowOptions.coverOrigin.y };
      originElevation = this.workflowOptions.coverOrigin.elevation ?? originElevation;
    } else if (template) {
      position = foundry.utils.duplicate(template.center);
      const dimensions = canvas.dimensions;
      if (template.document.t === "rect") {
        position.x += (template.document.width ?? 1) / (dimensions?.distance ?? 5) / 2 * (dimensions?.size ?? 100);
        position.y += (template.document.width ?? 1) / (dimensions?.distance ?? 5) / 2 * (dimensions?.size ?? 100);
      }
      originElevation = template.document.elevation;
    }
    if (position) {
      if (configSettings.optionalRules.coverCalculation === "levelsautocover" && installedModules.get("levelsautocover")) {
        moduleCoverBonus = computeCoverBonus({
          center: position,
          x: position.x,
          y: position.y,
          id: this.token?.id,
          elevation: originElevation,
          getCenterPoint() {
            return { x: position.x, y: position.y };
          },
          document: {
            x: position.x,
            y: position.y,
            elevation: originElevation,
            disposition: targetDocument?.disposition,
            object: { id: this.token?.id }
          }
        }, target, this.activity);
      } else if (configSettings.optionalRules.coverCalculation === "simbuls-cover-calculator" && installedModules.get("simbuls-cover-calculator")) {
        const coverData = await globalThis.CoverCalculator.checkCoverViaCoordinates(
          position.x,
          position.y,
          false,
          "AoE",
          false,
          target
        );
        if (coverData?.data.results.cover === 3) moduleCoverBonus = TOTAL_COVER;
        else moduleCoverBonus = -coverData.data.results.value;
      } else if (configSettings.optionalRules.coverCalculation === "tokencover" && installedModules.get("tokencover")) {
        if (this.token) moduleCoverBonus = computeCoverBonus(foundry.utils.deepClone(this.token, { center: position }), target, this.activity);
      } else if (configSettings.optionalRules.coverCalculation === "simplecover5e" && installedModules.get("simplecover5e")) {
        const syntheticAttacker = {
          center: position,
          scene: canvas.scene,
          id: this.token?.id,
          x: position.x,
          y: position.y,
          elevation: originElevation,
          getCenterPoint() {
            return { x: position.x, y: position.y };
          },
          document: {
            x: position.x,
            y: position.y,
            elevation: originElevation,
            disposition: targetDocument?.disposition,
            getCenterPoint() {
              return { x: position.x, y: position.y };
            }
          }
        };
        moduleCoverBonus = computeCoverBonus(syntheticAttacker, target, this.activity);
      }
    } else if (this.token) {
      moduleCoverBonus = computeCoverBonus(this.token, target, this.activity);
    }
    const effectiveCoverBonus = Math.max(moduleCoverBonus, statusCoverBonus);
    if (this.activity.actionType === "rsak" && this.actor.flags?.dnd5e?.spellSniper && effectiveCoverBonus !== TOTAL_COVER) {
      return -statusCoverBonus;
    }
    if (effectiveCoverBonus === TOTAL_COVER) {
      if (this.activity.midiProperties?.ignoreFullCover) {
        return Math.max(THREE_QUARTERS_COVER - statusCoverBonus, 0);
      }
      return TOTAL_COVER;
    }
    return Math.max(moduleCoverBonus - statusCoverBonus, 0);
  }
  /**
   * Determines if a save roll is a critical success (natural 20) or fumble (natural 1)
   * @param saveRoll The save roll to check
   * @param fallbackTotal Fallback total if dice results can't be read
   * @returns Object with isCritical and isFumble booleans
   */
  determineCriticalOrFumble(saveRoll, fallbackTotal) {
    if (!saveRoll?.terms) {
      return { isCritical: false, isFumble: false };
    }
    const dterm = saveRoll.terms[0];
    const diceRoll = dterm?.results?.find((result) => result.active)?.result ?? fallbackTotal;
    const isFumble = diceRoll <= (dterm.options?.fumble ?? 1);
    const isCritical = diceRoll >= (dterm.options?.critical ?? 20);
    return { isCritical, isFumble };
  }
  /**
   * Updates the advantageSaves and disadvantageSaves sets based on the roll result
   * @param target The target token
   * @param result The roll result array
   */
  updateAdvantageTrackingFromRoll(target, result) {
    const advantageMode = result[0]?.options?.advantageMode;
    if (advantageMode !== void 0) {
      if (advantageMode === 1) this.advantageSaves.add(target);
      else this.advantageSaves.delete(target);
      if (advantageMode === -1) this.disadvantageSaves?.add(target);
      else this.disadvantageSaves?.delete(target);
    } else {
      if (result[0]?.terms?.[0]?.options?.advantage) this.advantageSaves.add(target);
      else this.advantageSaves.delete(target);
      if (result[0]?.terms?.[0]?.options?.disadvantage) this.disadvantageSaves?.add(target);
      else this.disadvantageSaves?.delete(target);
    }
    if (this.advantageSaves.has(target) && this.disadvantageSaves?.has(target)) {
      this.advantageSaves.delete(target);
      this.disadvantageSaves.delete(target);
    }
    this.advantageSaves = this.advantageSaves;
  }
  /**
   * Determines the roll type, action, and related properties based on the save activity
   * @returns Object containing rollAction, rollType, rollAbilities, rollSkills, rollTools, and flagRollType
   */
  determineRollTypeAndAction() {
    let rollAction;
    let rollType;
    let rollAbilities = [];
    let rollSkills = [];
    let rollTools = [];
    let flagRollType;
    if (this.saveActivity?.save) {
      rollType = "save";
      flagRollType = "save";
      if (this.saveActivity.save.ability instanceof Set) {
        rollAbilities = Array.from(this.saveActivity.save.ability);
      } else {
        const ability = this.saveActivity.save.ability;
        if (ability) rollAbilities = [ability];
      }
      rollAction = CONFIG.Actor.documentClass.prototype.rollSavingThrow;
    } else if (this.saveActivity?.check) {
      const associatedArray = Array.from(this.saveActivity.check.associated);
      if (associatedArray.length === 0 && this.item.type === "tool") {
        const baseItem = this.item.system.type.baseItem;
        if (baseItem) associatedArray.push(baseItem);
      }
      if (associatedArray.length === 0) {
        rollType = "check";
        const ability = this.saveActivity.check.ability;
        if (ability) rollAbilities = [ability];
        rollAction = CONFIG.Actor.documentClass.prototype.rollAbilityCheck;
        flagRollType = "check";
      } else {
        const skills = associatedArray.filter((id) => !!GameSystemConfig.skills[id]);
        if (skills.length > 0 && skills.length === associatedArray.length) {
          rollType = "skill";
          rollSkills = skills;
          const ability = this.saveActivity?.getAbility?.(skills[0]);
          if (ability) rollAbilities = [ability];
          flagRollType = "skill";
          rollAction = CONFIG.Actor.documentClass.prototype.rollSkill;
        } else if (skills.length === 0) {
          rollAction = CONFIG.Actor.documentClass.prototype.rollToolCheck;
          rollType = "tool";
          rollTools = associatedArray;
          const ability = this.saveActivity?.getAbility?.(associatedArray[0]);
          if (ability) rollAbilities = [ability];
          flagRollType = "tool";
        } else {
          const first = associatedArray[0];
          if (GameSystemConfig.skills[first]) {
            rollType = "skill";
            rollSkills = [first];
            const ability = this.saveActivity?.getAbility?.(first);
            if (ability) rollAbilities = [ability];
            flagRollType = "skill";
            rollAction = CONFIG.Actor.documentClass.prototype.rollSkill;
          } else {
            rollAction = CONFIG.Actor.documentClass.prototype.rollToolCheck;
            rollType = "tool";
            rollTools = [first];
            const ability = this.saveActivity?.getAbility?.(first);
            if (ability) rollAbilities = [ability];
            flagRollType = "tool";
          }
        }
      }
    }
    return { rollAction, rollType, rollAbilities, rollSkills, rollTools, flagRollType };
  }
  /**
   * Build a Monks Token Bar save request for a target
   */
  buildMonksTokenBarRequest(target, saveDetails) {
    return {
      token: target.id ?? "",
      advantage: saveDetails.advantage,
      disadvantage: saveDetails.disadvantage
    };
  }
  /**
   * Build a Flash Rolls 5e save request for a target
   */
  buildFlashRollsRequest(target, saveDetails) {
    return {
      token: target.id ?? "",
      advantage: saveDetails.advantage,
      disadvantage: saveDetails.disadvantage
    };
  }
  /**
   * Build an Epic Rolls 5e save request for a target
   */
  buildEpicRollsRequest(target, saveDetails) {
    return {
      actorUuid: target.actor.uuid,
      advantage: saveDetails.advantage,
      disadvantage: saveDetails.disadvantage
    };
  }
  /**
   * Dispatch save requests to Monks Token Bar
   */
  dispatchMonksTokenBarSaves(monkRequestsPlayer, monkRequestsGM, rollType, rollAbilities, rollSkills, rollDC, whisper) {
    const monkRequests = monkRequestsPlayer.concat(monkRequestsGM);
    if (monkRequests.length === 0) return;
    let request;
    if (rollType === "save") {
      request = `save:${rollAbilities[0]}`;
    } else if (rollType === "check") {
      request = rollAbilities[0] ? `ability:${rollAbilities[0]}` : `dice:1d20`;
    } else if (rollType === "skill") {
      request = `skill:${rollSkills[0]}`;
    }
    const showdc = shouldDisplaySaveDC(this.actor);
    const buildRequestData = /* @__PURE__ */ __name((tokenData, rollMode) => {
      const requestData = {
        tokenData,
        request,
        silent: true,
        showdc,
        rollMode
      };
      if (showdc && "whisper" !== configSettings.autoCheckSaves) {
        requestData.dc = rollDC;
      }
      return requestData;
    }, "buildRequestData");
    if (!whisper) {
      const rollMode = (this.activity.midiProperties?.rollMode ?? "default") !== "default" ? this.activity.midiProperties.rollMode : CONST.DICE_ROLL_MODES.PUBLIC;
      timedExecuteAsGM("monksTokenBarSaves", buildRequestData(monkRequests, rollMode));
    } else {
      const playerRollMode = (this.activity.midiProperties?.rollMode ?? "default") !== "default" ? this.activity.midiProperties.rollMode : "roll";
      if (monkRequestsPlayer.length > 0) {
        timedExecuteAsGM("monksTokenBarSaves", buildRequestData(monkRequestsPlayer, playerRollMode));
      }
      if (monkRequestsGM.length > 0) {
        timedExecuteAsGM("monksTokenBarSaves", buildRequestData(monkRequestsGM, CONST.DICE_ROLL_MODES.PRIVATE));
      }
    }
  }
  /**
   * Dispatch save requests to Flash Rolls 5e
   */
  dispatchFlashRollsSaves(flashRequestsPlayer, flashRequestsGM, rollType, rollAbilities, rollSkills, rollTools, rollDC) {
    const flashRequests = flashRequestsPlayer.concat(flashRequestsGM);
    if (flashRequests.length === 0) return;
    let flashRequestType;
    if (rollType === "save") {
      flashRequestType = "savingthrow";
    } else if (rollType === "check") {
      flashRequestType = "ability";
    } else if (rollType === "skill") {
      flashRequestType = "skill";
    } else if (rollType === "tool") {
      flashRequestType = "tool";
    } else {
      flashRequestType = "savingthrow";
    }
    const flashRollKey = rollAbilities?.[0] ?? rollSkills?.[0] ?? rollTools?.[0];
    if (!flashRollKey) {
      flashRequestType = "custom";
    }
    const flashRequest = {
      requestType: flashRequestType,
      rollKey: flashRollKey ?? "1d20",
      actorIds: flashRequests.map((request) => request.token),
      dc: rollDC,
      advantage: flashRequests.some((r) => r.advantage) || void 0,
      disadvantage: flashRequests.some((r) => r.disadvantage) || void 0,
      skipRollDialog: true,
      sendAsRequest: true,
      rollMode: (this.activity.midiProperties?.rollMode ?? "default") !== "default" ? this.activity.midiProperties.rollMode : "roll",
      workflowId: this.id
    };
    timedExecuteAsGM("flashTokenBarSaves", flashRequest);
  }
  /**
   * Dispatch save requests to Epic Rolls 5e
   */
  dispatchEpicRollsSaves(rerRequestsPlayer, rerRequestsGM, rollType, rollDC) {
    const BasicRoll = CONFIG.Dice.BasicRoll;
    const rerRequests = rerRequestsPlayer.concat(rerRequestsGM);
    if (rerRequests.length === 0) return;
    const rerType = rollType;
    const abilityOrAssociated = this.saveActivity?.save?.ability.first() ?? this.saveActivity?.check?.associated.first();
    const type = abilityOrAssociated ? `${rerType}.${abilityOrAssociated}` : "custom";
    const rerRequest = {
      actors: rerRequests.map((request) => request.actorUuid),
      contestants: [],
      type,
      options: {
        DC: rollDC,
        showDC: shouldDisplaySaveDC(this.actor),
        blindRoll: configSettings.autoCheckSaves === "whisper",
        showRollResults: false,
        hideNames: true,
        noMessage: true,
        ...abilityOrAssociated ? {} : { formula: "1d20" },
        rollSettings: rerRequests.map((request) => ({
          advantage: request.advantage,
          disadvantage: request.disadvantage,
          uuid: request.actorUuid
        }))
      }
    };
    ui.EpicRolls5e.requestRoll(rerRequest).then((rerResult) => {
      if (rerResult.cancelled) {
        let roll = new BasicRoll("-1");
        roll = roll.evaluateSync({ strict: false });
        for (let uuid of rerRequest.actors) {
          const fn = this.saveRequests[uuid];
          delete this.saveRequests[uuid];
          fn(roll);
        }
      } else {
        for (let rerRoll of rerResult.results) {
          const actorUuid = rerRoll.actor.uuid;
          const fn = this.saveRequests[actorUuid];
          delete this.saveRequests[actorUuid];
          const roll = Roll.fromJSON(JSON.stringify(rerRoll.roll));
          fn(roll);
        }
      }
    });
  }
  /**
   * Create a player chat save request with timeout handling
   */
  createPlayerChatSaveRequest(target, player, saveDetails, rollType, rollAbilities, rollSkills, rollTools, rollDC, showRoll, GMprompt, isMagicSave, rollAction) {
    const rollAbility2 = rollAbilities[0];
    const rollSkill = rollSkills[0];
    const rollTool = rollTools[0];
    const BasicRoll = CONFIG.Dice.BasicRoll;
    const requestId = target?.id ?? foundry.utils.randomID();
    const saveDisplay = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
    const promise = new Promise((resolve) => {
      this.saveRequests[requestId] = resolve;
      if (configSettings.playerSaveTimeout > 0) {
        this.saveTimeouts[requestId] = setTimeout(async () => {
          if (this.saveRequests[requestId]) {
            delete this.saveRequests[requestId];
            delete this.saveTimeouts[requestId];
            let result;
            const showTargetDC = configSettings.autoCheckSaves !== "whisper" && shouldDisplaySaveDC(this.actor);
            if (!game.user?.isGM && saveDisplay === "allShow") {
              const rollMode = (this.activity.midiProperties?.rollMode ?? "default") !== "default" ? this.activity.midiProperties?.rollMode : CONST.DICE_ROLL_MODES.PUBLIC;
              result = await timedAwaitExecuteAsGM("rollAbility", {
                saveDetails,
                displayOptions: {
                  fastForward: true,
                  chatMessage: showRoll,
                  rollMode,
                  showTargetDC
                }
              });
            } else {
              let config = {
                target: showTargetDC ? rollDC : void 0,
                ability: rollAbility2,
                skill: rollSkill,
                midiOptions: {
                  workflowId: this.id,
                  itemCardUuid: this.itemCardUuid,
                  advantage: saveDetails.advantage,
                  disadvantage: saveDetails.disadvantage,
                  mapKeys: false,
                  // Timeout: auto-resolve with best choice, patching.ts handles multi-choice auto-selection
                  fastForward: true,
                  recomputeAdvantage: true,
                  // compute advantage/disadvantage from flags in roll function
                  saveItemUuid: this.saveItem?.uuid,
                  isConcentrationCheck: saveDetails.isConcentrationCheck,
                  workflowOptions: saveDetails.workflowOptions,
                  rollAbilities,
                  rollSkills,
                  rollTools,
                  advantageByChoice: saveDetails.advantageByChoice
                }
              };
              let dialog = { ability: rollAbility2, skill: rollSkill, tool: rollTool };
              let message = { create: showRoll };
              if (rollType === "tool") {
                let tool = target.actor?.items.get(this.saveActivity?.item.id ?? "");
                if (!tool) tool = target.actor?.items.find((i) => i.type === "tool" && i.system.type.baseItem === rollTool);
                if (!tool) {
                  resolve([await new BasicRoll("-1").evaluate()]);
                  return;
                } else {
                  config.bonus = tool.system.bonus;
                  config.prof = tool.system.prof;
                  config.item = tool;
                }
              }
              result = await rollAction.bind(target.actor)(config, dialog, message);
              resolve(result);
            }
          }
        }, (configSettings.playerSaveTimeout || 1) * 1e3);
      }
    });
    const requestCard = requestPCSave(
      rollAbility2 ?? "",
      rollType,
      player.id,
      target.actor,
      {
        flavor: this.item.name ?? "",
        requestId,
        GMprompt,
        isMagicSave: !!isMagicSave,
        rollAbilities,
        rollSkills,
        rollTools,
        saveDetails
      }
    );
    return { promise, requestCard };
  }
  /**
   * Create a standard socketlib save roll
   */
  createStandardSaveRoll(target, saveDetails, whisper, showRoll, showRollDialog, simulate) {
    const { rollType, rollAbilities = [] } = saveDetails;
    if (rollType === "save" && rollAbilities.length === 0) {
      console.warn(`midi-qol | createStandardSaveRoll called with rollType=save but no abilities`);
      return Promise.resolve(null);
    }
    let owner = playerFor(target);
    if (!owner?.isGM && owner?.active) showRoll = true;
    if (!owner?.active) owner = preferredActiveGM();
    if (!owner) owner = game.user;
    let rollMode = (this.activity.midiProperties?.rollMode ?? "default") !== "default" ? this.activity.midiProperties.rollMode : whisper ? CONST.DICE_ROLL_MODES.PRIVATE : CONST.DICE_ROLL_MODES.PUBLIC;
    if (!owner?.isGM) {
      if ((this.activity.midiProperties?.rollMode ?? "default") === "default") {
        if (["noneDialogPublic", "nonePublic"].includes(configSettings.playerRollSaves)) {
          rollMode = CONST.DICE_ROLL_MODES.PUBLIC;
        } else if (["noneDialogPrivate", "none"].includes(configSettings.playerRollSaves)) {
          rollMode = CONST.DICE_ROLL_MODES.PRIVATE;
        } else if (configSettings.playerRollSaves === "noneDialogSelf") {
          rollMode = CONST.DICE_ROLL_MODES.SELF;
        } else if (configSettings.playerRollSaves === "noneDialogBlind") {
          rollMode = CONST.DICE_ROLL_MODES.BLIND;
        }
      }
    }
    const showTargetDC = owner?.isGM || configSettings.autoCheckSaves !== "whisper" && shouldDisplaySaveDC(this.actor);
    return socketlibSocket.executeAsUser("rollAbility", owner?.id, {
      saveDetails,
      displayOptions: {
        fastForward: simulate || !showRollDialog,
        chatMessage: showRoll,
        rollMode,
        showTargetDC
      }
    });
  }
  /**
   * Queue a target's save roll to the appropriate handler based on settings
   * Returns the promise and optionally adds to module-specific request arrays
   */
  queueTargetSaveRoll(options) {
    const { target, saveDetails, rollInfo, isMagicSave, displayOptions, playerInfo, moduleFlags, moduleRequests } = options;
    const { type: rollType, abilities: rollAbilities, skills: rollSkills, tools: rollTools, action: rollAction } = rollInfo;
    const { whisper, showRoll, showRollDialog, simulate } = displayOptions;
    const { player, GMprompt, playerChat } = playerInfo;
    const { playerMonksTB, playerFlashTB, playerEpicRolls, gmMonksTB, gmFlashTB, gmRER } = moduleFlags;
    const friendlyRoll = this.getFriendlyTargetRoll(saveDetails);
    if (friendlyRoll) {
      return { promise: friendlyRoll, requestCard: void 0 };
    }
    if (!player?.isGM && playerMonksTB || player?.isGM && gmMonksTB) {
      const promise2 = new Promise((resolve) => {
        let requestId = target.id ?? foundry.utils.randomID();
        this.saveRequests[requestId] = resolve;
      });
      const requests = player?.isGM ? moduleRequests.monks.gm : moduleRequests.monks.player;
      requests.push(this.buildMonksTokenBarRequest(target, saveDetails));
      return { promise: promise2, requestCard: void 0 };
    }
    if (!player?.isGM && playerFlashTB || player?.isGM && gmFlashTB) {
      const promise2 = new Promise((resolve) => {
        let requestId = target.actor?.uuid ?? foundry.utils.randomID();
        this.saveRequests[requestId] = resolve;
      });
      const requests = player?.isGM ? moduleRequests.flash.gm : moduleRequests.flash.player;
      requests.push(this.buildFlashRollsRequest(target, saveDetails));
      return { promise: promise2, requestCard: void 0 };
    }
    if (!player?.isGM && playerEpicRolls || player?.isGM && gmRER) {
      const promise2 = new Promise((resolve) => {
        let requestId = target.actor?.uuid ?? foundry.utils.randomID();
        this.saveRequests[requestId] = resolve;
      });
      const requests = player?.isGM ? moduleRequests.epic.gm : moduleRequests.epic.player;
      requests.push(this.buildEpicRollsRequest(target, saveDetails));
      return { promise: promise2, requestCard: void 0 };
    }
    if (player?.active && playerChat || player?.isGM && GMprompt === "none") {
      if (debugEnabled > 0) {
        warn(`checkSaves | Player ${player?.name} controls actor ${target.actor.name} - requesting ${rollAbilities[0]} ${rollType}`);
      }
      const result = this.createPlayerChatSaveRequest(
        target,
        player,
        saveDetails,
        rollType,
        rollAbilities,
        rollSkills,
        rollTools,
        saveDetails.rollDC,
        showRoll,
        GMprompt,
        isMagicSave,
        rollAction
      );
      return { promise: result.promise, requestCard: result.requestCard };
    }
    const promise = this.createStandardSaveRoll(
      target,
      saveDetails,
      whisper,
      showRoll,
      showRollDialog,
      simulate
    );
    return { promise, requestCard: void 0 };
  }
  /**
   * Process a single target's save result - determine if saved, apply bonuses, update tracking sets
   */
  async processTargetSaveResult(target, result, context) {
    const { rollDC, rollAbility: rollAbility2, rollType, template, D20Roll } = context;
    let saveRollTotal = result.reduce((acc, r) => acc + r.total, 0);
    let saveRolls = result;
    this.updateAdvantageTrackingFromRoll(target, result);
    let { isFumble, isCritical } = this.determineCriticalOrFumble(saveRolls[0], saveRollTotal);
    const coverSaveBonus = await this.calculateCoverSaveBonus(target, template, rollAbility2);
    if (coverSaveBonus > 0 && coverSaveBonus < TOTAL_COVER && saveRolls[0]) {
      const OperatorTerm = foundry.dice.terms.OperatorTerm;
      const NumericTerm2 = foundry.dice.terms.NumericTerm;
      saveRolls[0].terms.push(
        new OperatorTerm({ operator: "+" }),
        new NumericTerm2({ number: coverSaveBonus, options: { flavor: i18n("DND5E.Cover") } })
      );
      saveRolls[0].resetFormula();
      saveRolls[0]._total += coverSaveBonus;
      saveRollTotal += coverSaveBonus;
    }
    let resultDC = rollDC ?? 0;
    if (Number.isNumeric(result[0]?.options.target)) resultDC = result[0].options.target;
    let saved = saveRollTotal >= resultDC;
    if (checkRule("criticalSaves")) {
      saved = (isCritical || saveRollTotal >= resultDC) && !isFumble;
    }
    if (this.checkSculptOrCarefulSpells(target)) {
      saved = true;
    }
    if (coverSaveBonus >= TOTAL_COVER) {
      saved = true;
    }
    this.updateSaveFailSets(target, saved);
    if (!this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
      if (saved)
        await doReactions(target, this.tokenUuid, this.attackRoll, "reactionsavesuccess", { workflow: this, activity: this.activity, item: this.item });
      else
        await doReactions(target, this.tokenUuid, this.attackRoll, "reactionsavefail", { workflow: this, activity: this.activity, item: this.item });
    }
    if (isCritical) this.criticalSaves = this.criticalSaves.add(target);
    if (configSettings.allowUseMacro && this.workflowOptions.noTargetOnUseMacro !== true) {
      const rollResults = await this.triggerTargetMacros(["isSave", "isSaveSuccess", "isSaveFailure"], /* @__PURE__ */ new Set([target]), { saved });
      const newRolls = rollResults[target.document.uuid];
      if (newRolls?.[0] instanceof Roll) {
        saveRolls = newRolls;
        saveRollTotal = newRolls.reduce((acc, r) => acc + (r?.total ?? 0), 0);
        saved = saveRollTotal >= resultDC;
        ({ isFumble, isCritical } = this.determineCriticalOrFumble(saveRolls[0], saveRollTotal));
      }
    }
    if (!(result[0] instanceof D20Roll)) result[0] = D20Roll.fromRoll(result[0]);
    if (!saved && !Number.isNumeric(result[0].options.target)) {
      const failBonusResult = await this.applyFailedSaveBonuses(target, result, saveRolls, rollType ?? "", rollAbility2 ?? "");
      if (failBonusResult) {
        saveRolls = failBonusResult.saveRolls;
        saveRollTotal = failBonusResult.saveRollTotal;
        saved = saveRollTotal >= resultDC;
        ({ isFumble, isCritical } = this.determineCriticalOrFumble(saveRolls[0], saveRollTotal));
      }
    }
    if (isFumble) this.fumbleSaves = this.fumbleSaves.add(target);
    if (isCritical) this.criticalSaves = this.criticalSaves.add(target);
    const saveAbilities = rollAbility2 ? /* @__PURE__ */ new Set([rollAbility2]) : this.saveActivity?.save?.ability;
    if (await this.checkSuperSaver(target, saveAbilities))
      this.superSavers = this.superSavers.add(target);
    if (await this.checkSemiSuperSaver(target, saveAbilities))
      this.semiSuperSavers = this.semiSuperSavers.add(target);
    const concentrationResult = await this.applyConcentrationSaveBonus(target, saveRolls[0], resultDC, isCritical, isFumble);
    if (concentrationResult) {
      saveRolls[0] = concentrationResult.saveRoll;
      saveRollTotal = concentrationResult.saveRollTotal;
      saved = concentrationResult.saved;
    }
    this.updateSaveFailSets(target, saved);
    if (game.user?.isGM) log(`Ability save/check: ${target.name} rolled ${saveRollTotal} vs ${rollAbility2} DC ${rollDC}`);
    await this.buildSaveDisplayEntry(target, saveRolls[0], saved, rollDC, coverSaveBonus);
  }
  /**
   * Update the saves and failedSaves sets based on whether the target saved
   */
  updateSaveFailSets(target, saved) {
    if (saved) {
      this.saves = this.saves.add(target);
      this.failedSaves.delete(target);
      this.failedSaves = this.failedSaves;
    } else {
      this.saves.delete(target);
      this.saves = this.saves;
      this.failedSaves = this.failedSaves.add(target);
    }
  }
  /**
   * Apply fail bonuses (flags.midi-qol.save.fail) if the save failed
   */
  async applyFailedSaveBonuses(target, result, saveRolls, rollType, rollAbility2) {
    const failFlagsLength = collectBonusFlags(target.actor, rollType, "fail.all").length;
    const failAbilityFlagsLength = collectBonusFlags(target.actor, rollType, `fail.${rollAbility2}`).length;
    if (!failFlagsLength && !failAbilityFlagsLength) return null;
    let owner = playerFor(target);
    if (!owner?.active) owner = game.users?.find((u) => u.isGM && u.active);
    if (!owner) return null;
    let newRoll;
    if (owner?.isGM && game.user?.isGM) {
      newRoll = await bonusCheck(target.actor, result[0], rollType, failAbilityFlagsLength ? `fail.${rollAbility2}` : "fail.all");
    } else {
      newRoll = await socketlibSocket.executeAsUser("bonusCheck", owner?.id, {
        actorUuid: target.actor.uuid,
        result: JSON.stringify(result[0].toJSON()),
        rollType,
        selector: failFlagsLength ? "fail.all" : `fail.${rollAbility2}`
      });
    }
    const updatedSaveRolls = [...saveRolls];
    updatedSaveRolls[0] = Roll.fromJSON(JSON.stringify(newRoll));
    const saveRollTotal = updatedSaveRolls.reduce((acc, r) => acc + r.total, 0);
    return { saveRolls: updatedSaveRolls, saveRollTotal };
  }
  /**
   * Compute module integration settings for save rolls (Monks TB, Flash Rolls, Epic Rolls)
   */
  computeModuleIntegrationSettings(simulate, flagRollType) {
    const playerMonksTB = !simulate && flagRollType !== "tool" && !!installedModules.get("monks-tokenbar") && configSettings.playerRollSaves === "mtb";
    const playerFlashTB = !simulate && !!installedModules.get("flash-rolls-5e") && configSettings.playerRollSaves === "ftb";
    const playerEpicRolls = !simulate && !!installedModules.get("epic-rolls-5e") && configSettings.playerRollSaves === "rer";
    return {
      flags: {
        playerMonksTB,
        playerFlashTB,
        playerEpicRolls,
        gmMonksTB: false,
        // Set per-target in determineTargetRollSettings
        gmFlashTB: false,
        gmRER: false
      },
      requests: {
        monks: { player: [], gm: [] },
        flash: { player: [], gm: [] },
        epic: { player: [], gm: [] }
      }
    };
  }
  /**
   * Build SaveDetails for a target, including magic resistance/vulnerability and advantage checks.
   * When multiple choices are available (abilities, skills, or tools), pre-computes advantage
   * for each option and stores results in saveDetails.advantageByChoice.
   * A roll is always ONE type, so a single map suffices.
   */
  async buildSaveDetailsForTarget(target, context) {
    const { rollDC, rollType, isMagicSave, actorDisposition, rollAbilities, rollSkills, rollTools } = context;
    const targetDocument = target.document;
    const saveDetails = {
      actorUuid: target.actor?.uuid ?? "",
      advantage: void 0,
      disadvantage: void 0,
      modifierTracker: void 0,
      isMagicSave: !!isMagicSave,
      isFriendly: targetDocument?.disposition === actorDisposition,
      isConcentrationCheck: void 0,
      rollDC: rollDC ?? 0,
      saveItemUuid: this.saveItem?.uuid ?? "",
      workflowId: this.id,
      itemCardUuid: this.itemCardUuid ?? "",
      workflowOptions: this.workflowOptions,
      rollType: rollType ?? "save",
      rollAbilities,
      rollSkills,
      rollTools,
      itemId: this.saveActivity?.item.id ?? ""
    };
    const abilityId = this.saveActivity.save?.ability?.first() ?? this.saveActivity.check?.ability ?? "";
    const preComputedMap = await buildPreComputedAdvantageMap(
      target.actor,
      rollType ?? "",
      rollAbilities,
      { workflow: this, isMagicSave: !!isMagicSave }
    );
    const defaultAbilityData = preComputedMap[abilityId];
    saveDetails.modifierTracker = defaultAbilityData?.tracker instanceof RollModifierTracker ? defaultAbilityData.tracker : void 0;
    saveDetails.advantage = defaultAbilityData?.hasAdvantage;
    saveDetails.disadvantage = defaultAbilityData?.hasDisadvantage;
    saveDetails.isConcentrationCheck = this.item.flags?.[MODULE_ID]?.isConcentrationCheck;
    const multipleChoices = rollAbilities.length > 1 || rollSkills.length > 1 || rollTools.length > 1;
    if (multipleChoices) {
      let choiceMap;
      if (rollAbilities.length > 1) {
        choiceMap = preComputedMap;
      } else if (rollSkills.length > 1) {
        choiceMap = await buildPreComputedAdvantageMap(
          target.actor,
          "skill",
          rollSkills,
          { workflow: this, isMagicSave: false }
        );
      } else {
        choiceMap = await buildPreComputedAdvantageMap(
          target.actor,
          "tool",
          rollTools,
          { workflow: this, isMagicSave: false }
        );
      }
      saveDetails.advantageByChoice = choiceMap;
    }
    if (saveDetails.isFriendly && saveDetails.modifierTracker) {
      const descriptionIncludesAutoFail = this.item.system.description?.value?.toLowerCase().includes(i18n("midi-qol.autoFailFriendly")?.toLowerCase());
      if (descriptionIncludesAutoFail || this.activity.friendlySave === "friendlyFail") {
        saveDetails.modifierTracker.addAttribution("FAIL", "friendlyFail", i18n("midi-qol.FriendlyAutoFail"));
      } else if (this.activity.friendlySave === "friendlySuccess") {
        saveDetails.modifierTracker.addAttribution("SUCCESS", "friendlySuccess", i18n("midi-qol.FriendlyAutoSuccess"));
      }
    }
    if (saveDetails.advantage && !saveDetails.disadvantage) this.advantageSaves = this.advantageSaves.add(target);
    else if (saveDetails.disadvantage && !saveDetails.advantage) this.disadvantageSaves?.add(target);
    return saveDetails;
  }
  /**
   * Normalize roll results to ensure they are all Roll[] arrays
   */
  normalizeRollResults(results, D20Roll) {
    const normalized = [];
    for (let i = 0; i < results.length; i++) {
      const currResult = results[i];
      if (currResult instanceof Roll) {
        normalized[i] = [currResult];
      } else if (currResult instanceof Array) {
        normalized[i] = currResult.map((r) => {
          if (!(r instanceof Roll)) {
            return D20Roll.fromJSON(JSON.stringify(r));
          }
          return r;
        });
      } else {
        normalized[i] = [];
      }
    }
    return normalized;
  }
  /**
   * update this.saves to be a Set of successful saves from the set of tokens this.hitTargets and failed saves to be the complement
   */
  async checkSaves(whisper = false, simulate = false) {
    this.initSaveResults();
    this.targetSaveDetails = {};
    if (debugEnabled > 1) debug(`checkSaves: whisper ${whisper}  hit targets ${this.hitTargets}`);
    if (this.hitTargets.size <= 0 && this.hitTargetsEC.size <= 0) {
      this.saveDisplayFlavor = `<span>${i18n("midi-qol.noSaveTargets")}</span>`;
      return;
    }
    if (!this.saveActivity) return;
    const allHitTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
    if (allHitTargets.size === 0) return;
    let rollDC = this.saveActivity?.save?.dc.value ?? this.saveActivity?.check?.dc.value;
    const D20Roll = CONFIG.Dice.D20Roll;
    this.saveDC = rollDC;
    const requestCards = [];
    let promises = [];
    const { rollAction, rollType, rollAbilities, rollSkills, rollTools, flagRollType } = this.determineRollTypeAndAction();
    if (!rollAction) return;
    if (rollType === "save" && rollAbilities.length === 0) {
      console.warn(`midi-qol | ${this.item.name} ${this.saveActivity?.name} has a save activity but no ability configured`);
      return;
    }
    const { flags: moduleFlags, requests: moduleRequests } = this.computeModuleIntegrationSettings(simulate, flagRollType ?? "");
    const { playerMonksTB, playerFlashTB, playerEpicRolls } = moduleFlags;
    const saveDisplay = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
    const isMagicSave = this.item.type === "spell" || this.activity.midiProperties?.magicEffect;
    try {
      let actorDisposition;
      if (this.token && this.token.document?.disposition !== void 0) actorDisposition = this.token.document.disposition;
      else {
        actorDisposition = this.actor.type === "npc" ? -1 : 1;
      }
      for (let target of allHitTargets) {
        if (!target || !target?.actor) continue;
        let showRoll = saveDisplay === "allShow";
        if (simulate) showRoll = false;
        if (!this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
          await doReactions(target, this.tokenUuid, this.attackRoll ?? new Roll(""), "reactionsave", { workflow: this, activity: this.activity, item: this.item });
        }
        const saveDetails = await this.buildSaveDetailsForTarget(target, {
          rollDC,
          rollType,
          rollAbilities,
          rollSkills,
          rollTools,
          flagRollType: flagRollType ?? "",
          isMagicSave: !!isMagicSave,
          actorDisposition
        });
        const { player, showRollDialog, GMprompt, gmMonksTB, gmFlashTB, gmRER, playerChat } = this.determineTargetRollSettings(target, flagRollType, simulate);
        this.saveDetails = saveDetails;
        const targetDocUuid = getTokenDocument(target)?.uuid;
        if (targetDocUuid && this.targetSaveDetails) this.targetSaveDetails[targetDocUuid] = saveDetails;
        await asyncHooksCallAll("midi-qol.preTargetSave", target, this, saveDetails);
        if (configSettings.allowUseMacro && this.workflowOptions.noTargetOnUseMacro !== true) await this.triggerTargetMacros(["preTargetSave"], /* @__PURE__ */ new Set([target]));
        const { promise, requestCard } = this.queueTargetSaveRoll({
          target,
          saveDetails,
          rollInfo: {
            type: rollType ?? "save",
            abilities: rollAbilities,
            skills: rollSkills,
            tools: rollTools,
            action: rollAction
          },
          isMagicSave: !!isMagicSave,
          displayOptions: {
            whisper,
            showRoll,
            showRollDialog,
            simulate
          },
          playerInfo: {
            player,
            GMprompt: typeof GMprompt === "string" ? GMprompt : "none",
            playerChat
          },
          moduleFlags: {
            playerMonksTB,
            playerFlashTB,
            playerEpicRolls,
            gmMonksTB: gmMonksTB ?? false,
            gmFlashTB: gmFlashTB ?? false,
            gmRER: gmRER ?? false
          },
          moduleRequests
        });
        if (promise) promises.push(promise);
        if (requestCard) requestCards.push(requestCard);
      }
    } catch (err) {
      TroubleShooter.recordError(err);
      console.warn(err);
    }
    this.dispatchMonksTokenBarSaves(
      moduleRequests.monks.player,
      moduleRequests.monks.gm,
      rollType ?? "save",
      rollAbilities,
      rollSkills,
      rollDC,
      whisper
    );
    this.dispatchFlashRollsSaves(
      moduleRequests.flash.player,
      moduleRequests.flash.gm,
      rollType ?? "save",
      rollAbilities,
      rollSkills,
      rollTools,
      rollDC
    );
    this.dispatchEpicRollsSaves(
      moduleRequests.epic.player,
      moduleRequests.epic.gm,
      rollType ?? "save",
      rollDC
    );
    if (debugEnabled > 1) debug("check saves: requests are ", this.saveRequests);
    const rawResults = await Promise.all(promises);
    (await Promise.all(requestCards)).forEach((chatMessage) => chatMessage?.delete());
    const epicRequests = moduleRequests.epic.player.concat(moduleRequests.epic.gm);
    if (epicRequests?.length > 0) await busyWait(10);
    const results = this.normalizeRollResults(rawResults, D20Roll);
    this.saveResults = results.filter((r) => !!r).flat();
    let template;
    if (this.templateUuid) {
      const templateDocument = await fromUuid(this.templateUuid);
      template = templateDocument?.object;
    }
    const processContext = { rollDC, rollAbility: rollAbilities[0], rollType, template, D20Roll };
    let i = 0;
    for (let target of allHitTargets) {
      if (!target.actor) continue;
      if (!results[i]?.[0] || results[i]?.[0]?.total === void 0) {
        const message = `Token ${target.name} could not roll save/check assuming 1`;
        error2(message, target);
        TroubleShooter.recordError(new Error(message), message);
        results[i] = [await new D20Roll("1").evaluate()];
      }
      const tmpResult = results[i];
      const result = tmpResult instanceof Array ? tmpResult : tmpResult ? [tmpResult] : [new D20Roll("1").evaluateSync()];
      const midiChosenId = result[0]?.options?.midiChosenId;
      if (midiChosenId) processContext.rollAbility = midiChosenId;
      await this.processTargetSaveResult(target, result, processContext);
      i++;
    }
    this.saveDisplayFlavor = await this.computeSaveDisplayFlavor(rollDC, rollType ?? "", { rollAbilities: rollAbilities ?? [], rollSkills: rollSkills ?? [], rollTools: rollTools ?? [] });
  }
  async computeSaveDisplayFlavor(rollDC, rollType, options) {
    let { rollAbilities, rollSkills, rollTools } = options;
    const allHitTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
    let DCString = i18n("DND5E.AbbreviationDC") ?? "DC";
    if (DCString === "DND5E.AbbreviationDC") DCString = "DC";
    DCString = `${DCString} ${rollDC}`;
    if ((rollDC ?? -1) === -1) DCString = "";
    const dcLabel = `<label class="midi-qol-saveDC">${DCString}</label>`;
    if (rollType === "save") {
      const throwLabel = i18n(allHitTargets.size > 1 ? "midi-qol.saving-throws" : "midi-qol.saving-throw");
      const lines = rollAbilities.map(
        (ability) => `${dcLabel} ${GameSystemConfig.abilities[ability]?.label ?? ability} ${throwLabel}`
      );
      return lines.join("<br>");
    } else if (rollType === "check") {
      if (rollAbilities.length === 0) {
        return `${dcLabel} ${i18n("midi-qol.plain-d20-roll")}:`;
      }
      const checkLabel = i18n(allHitTargets.size > 1 ? "midi-qol.ability-checks" : "midi-qol.ability-check");
      const lines = rollAbilities.map(
        (ability) => `${dcLabel} ${GameSystemConfig.abilities[ability]?.label ?? ability} ${checkLabel}:`
      );
      return lines.join("<br>");
    } else if (rollType === "skill") {
      const lines = rollSkills.map(
        (skill) => `${dcLabel} ${GameSystemConfig.skills[skill]?.label ?? skill}`
      );
      return lines.join("<br>");
    } else if (rollType === "tool") {
      const lines = [];
      for (const tool of rollTools) {
        const toolLabel = await game.system?.documents.Trait.keyLabel(`tool:${tool}`);
        lines.push(`${dcLabel} ${toolLabel}`);
      }
      return lines.join("<br>");
    }
    return "";
  }
  monksSavingCheck(message, update, _options, _userId) {
    if (!update.flags || !update.flags["monks-tokenbar"]) return true;
    const mflags = message.flags["monks-tokenbar"];
    for (let key of Object.keys(mflags)) {
      if (!key.startsWith("token")) continue;
      if (!mflags[key].roll) continue;
      const requestId = key.replace("token", "");
      if (!mflags[key].reveal) continue;
      if (this.saveRequests[requestId]) {
        let roll;
        try {
          roll = Roll.fromJSON(JSON.stringify(mflags[key].roll));
        } catch (err) {
          roll = foundry.utils.deepClone(mflags[key].roll);
        }
        const func = this.saveRequests[requestId];
        delete this.saveRequests[requestId];
        func(roll);
      }
    }
    return true;
  }
  flashSavingCheck(data) {
    if (debugEnabled > 0) debug(`flashSavingCheck | Received roll for ${data.actorUuid}`, data);
    if (debugEnabled > 0) debug(`flashSavingCheck | Available saveRequests keys:`, Object.keys(this.saveRequests));
    const requestId = data.actorUuid;
    if (!this.saveRequests[requestId]) {
      if (debugEnabled > 0) debug(`flashSavingCheck | No pending request for ${requestId}`);
      return;
    }
    const func = this.saveRequests[requestId];
    delete this.saveRequests[requestId];
    if (debugEnabled > 0) debug(`flashSavingCheck | Resolving save for ${requestId} with roll total ${data.total}`);
    func(data.roll);
  }
  async displayChatCardWithoutDamageDetail() {
    let chatMessage = getCachedDocument2(this.itemCardUuid);
    let data;
    if (chatMessage?.content) {
      let content = chatMessage.content;
      data = chatMessage.toObject();
      content = data.content || "";
      let searchRe = /<div[^>]*?class="midi-qol-damage-roll[^"]*"[\s\S]*?<div class="end-midi-qol-damage-roll">/;
      let replaceString = `<div class="midi-qol-damage-roll"><div class="end-midi-qol-damage-roll">`;
      content = content.replace(searchRe, replaceString);
      searchRe = /<div[^>]*?class="midi-qol-other-roll"[\s\S]*?<div class="end-midi-qol-other-roll">/;
      replaceString = `<div class="midi-qol-other-roll"><div class="end-midi-qol-other-roll">`;
      content = content.replace(searchRe, replaceString);
      searchRe = /<div[^>]*?class="midi-qol-bonus-roll">[\s\S]*?<div class="end-midi-qol-bonus-roll">/;
      replaceString = `<div class="midi-qol-bonus-roll"><div class="end-midi-qol-bonus-roll">`;
      content = content.replace(searchRe, replaceString);
    }
    if (installedModules.get("ready-set-roll-5e")) return;
    if (data && this.currentAction === this.WorkflowState_Completed) {
      if (this.itemCardUuid) {
        await _Workflow.removeItemCardButtons(this.itemCardUuid, { removeAllButtons: true });
      }
      delete data._id;
      const itemCard = await CONFIG.ChatMessage.documentClass.create(data);
      this.itemCardUuid = itemCard?.uuid;
    }
  }
  async callDamageHooksAndMacros(damages, token) {
    this.damageItem = damages;
    if (configSettings.allowUseMacro && !this.workflowOptions?.noTargetOnUseMacro && this.item.flags) {
      await this.triggerTargetMacros(["preTargetDamageApplication"], /* @__PURE__ */ new Set([token]));
    }
    await asyncHooksCallAll(`midi-qol.preTargetDamageApplication`, token, { item: this.item, workflow: this, damageItem: damages, ditem: damages });
    if ((damages.hpDamage !== 0 || damages.tempDamage > 0) && (this.hitTargets.has(token) || this.hitTargetsEC.has(token) || this.otherActivity?.save || this.otherActivity?.check)) {
      let healedDamaged = damages.hpDamage < 0 ? "isHealed" : "isDamaged";
      await asyncHooksCallAll(`midi-qol.${healedDamaged}`, token, { item: this.item, workflow: this, damageItem: damages, ditem: damages });
      const actorOnUseMacros = foundry.utils.getProperty(token.actor ?? {}, midiFlag2(`onUseMacroParts`)) ?? new OnUseMacros();
      if (this.callMacros) await this.callMacros(
        this.item,
        actorOnUseMacros?.getMacros(healedDamaged),
        "TargetOnUse",
        healedDamaged,
        { actor: token.actor, token }
      );
      healedDamaged = damages.hpDamage < 0 ? "isHealed" : "isDamaged";
      const expiredEffects = getAppliedEffects(token?.actor, { includeEnchantments: true }).filter((ef) => {
        const specialDuration = ef.flags?.dae?.specialDuration;
        if (!specialDuration) return false;
        return specialDuration.includes(healedDamaged);
      }).map((ef) => ef.uuid);
      if ((expiredEffects?.length ?? 0) > 0) {
        await timedAwaitExecuteAsGM("removeEffectUuids", {
          actorUuid: token.actor?.uuid,
          effects: expiredEffects,
          options: { "expiry-reason": `midi-qol:${healedDamaged}` }
        });
      }
    }
    if (configSettings.allowUseMacro && this.item.flags) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("preDamageApplication"), "OnUse", "preDamageApplication");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("preDamageApplication"), "OnUse", "preDamageApplication", damages);
    }
    Object.assign(damages, this.damageItem);
  }
  processDefenceRoll(message, _html, _data) {
    if (!this.defenceRequests) return true;
    const requestId = message?.speakerActor?.uuid;
    if (debugEnabled > 0) warn("processDefenceRoll |", requestId, this.saveRequests);
    if (!requestId) return true;
    if (!this.defenceRequests[requestId]) return true;
    clearTimeout(this.defenceTimeouts[requestId]);
    const handler = this.defenceRequests[requestId];
    if (!handler)
      return true;
    delete this.defenceRequests[requestId];
    delete this.defenceTimeouts[requestId];
    handler(message.rolls[0]);
    return true;
  }
  processSaveRoll(message, _html, _dataOrUserId) {
    if (!this.saveRequests) return {};
    const ddbglFlags = message.flags && message.flags["ddb-game-log"];
    const isDDBGL = ddbglFlags?.cls === "save" && !ddbglFlags?.pending;
    const isRoll = message.flags?.dnd5e?.messageType === "roll";
    let requestId = message?.speaker?.token;
    if (!requestId && isDDBGL) requestId = message?.speaker?.actor;
    if (debugEnabled > 0) warn("processSaveRoll |", requestId, this.saveRequests);
    if (!requestId) return true;
    if (!this.saveRequests[requestId] || !isRoll) return true;
    if (this.saveRequests[requestId]) {
      clearTimeout(this.saveTimeouts[requestId]);
      const handler = this.saveRequests[requestId];
      delete this.saveRequests[requestId];
      delete this.saveTimeouts[requestId];
      if (configSettings.undoWorkflow) {
        if (this.undoData) this.undoData.chatCardUuids.push(message.uuid);
        unTimedExecuteAsGM("updateUndoChatCardUuids", this.undoData);
      }
      handler(message.rolls[0]);
    }
    const saveDisplay = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
    if (game.user?.id !== message.author?.id && !["allShow"].includes(saveDisplay)) {
      setTimeout(() => ui.chat?.element.querySelector(`[data-message-id="${message.id}"]`)?.remove(), 100);
    }
    return true;
  }
  async checkSuperSaver(token, abilities) {
    if (!abilities) return false;
    for (let ability of abilities.values()) {
      const flags = token.actor?.flags?.[MODULE_ID]?.superSaver;
      if (!flags) return false;
      if (flags.all) {
        const flagVal = await evalActivationCondition2(this, flags.all, token, { errorReturn: false });
        if (flagVal) return true;
      }
      if (flags[ability]) {
        const flagVal = await evalActivationCondition2(this, flags[ability], token, { errorReturn: false });
        if (flagVal) return true;
      }
      if (this.actor.flags?.[MODULE_ID]?.sculptSpells && this.item.school === "evo" && this.preSelectedTargets.has(token)) {
        return true;
      }
    }
    return false;
  }
  async checkSemiSuperSaver(token, abilities) {
    if (!abilities) return false;
    for (let ability of abilities.values()) {
      const flags = token.actor?.flags?.[MODULE_ID]?.semiSuperSaver;
      if (!flags) return false;
      if (flags.all) {
        const flagVal = await evalActivationCondition2(this, flags.all, token, { errorReturn: false });
        if (flagVal) return true;
      }
      if (flags[ability]) {
        const flagVal = await evalActivationCondition2(this, flags[ability], token, { errorReturn: false });
        if (flagVal) return true;
      }
    }
    return false;
  }
  // TODO: Kill?
  // processCustomRoll(customRoll: any) {
  //   const formula = "1d20";
  //   const isSave = customRoll.fields.find(e => e[0] === "check");
  //   if (!isSave) return true;
  //   const rollEntry = customRoll.entries?.find((e) => e.type === "multiroll");
  //   let total = rollEntry?.entries?.find((e) => !e.ignored)?.total ?? -1;
  //   let advantage = rollEntry ? rollEntry.rollState === "highest" : undefined;
  //   let disadvantage = rollEntry ? rollEntry.rollState === "lowest" : undefined;
  //   return ({ total, formula, terms: [{ options: { advantage, disadvantage } }] });
  // }
  async processAttackRoll() {
    if (!this.attackRoll) return;
    const terms = this.attackRoll.terms;
    if (terms[0] instanceof foundry.dice.terms.NumericTerm) {
      this.diceRoll = Number(terms[0].total);
    } else {
      const dterm = terms[0];
      const activeResult = dterm.results?.find((d) => d.active);
      this.diceRoll = activeResult ? Number(activeResult.result) : Number(terms[0].total);
    }
    let critFumbleDiceRoll = this.diceRoll;
    if (this.attackRollModifierTracker.modifiers.willSucceed || this.attackRollModifierTracker.modifiers.willFail) {
      if (!(terms[0] instanceof foundry.dice.terms.NumericTerm)) {
        const dterm = terms[0];
        const activeResult = dterm.results?.find((d) => d.active);
        if (activeResult) critFumbleDiceRoll = Number(activeResult.result);
      }
    }
    let criticalThreshold = this.attackRoll.options.criticalSuccess ?? 20;
    if (this.targets.size > 0) {
      const midiFlags2 = this.targets.first()?.actor?.flags[MODULE_ID];
      let targetCrit = criticalThreshold;
      if (midiFlags2?.grants?.criticalThreshold) {
        const conditionData = createConditionData({ workflow: this, target: this.targets.first(), actor: this.actor });
        targetCrit = await evalCondition(midiFlags2.grants.criticalThreshold, conditionData, { errorReturn: criticalThreshold, async: true });
      }
      if (isNaN(targetCrit) || !Number.isNumeric(targetCrit)) targetCrit = criticalThreshold;
      criticalThreshold = Math.min(criticalThreshold, targetCrit);
    }
    const naturalCritical = critFumbleDiceRoll >= criticalThreshold;
    if (naturalCritical) {
      this.attackRollModifierTracker.critical.setNatural("naturalCrit", `${i18n("DND5E.Roll")} >= ${criticalThreshold}`);
    }
    const midiFumble = this.activity?.fumbleThreshold ?? 1;
    const attackRollFumbleTarget = this.attackRoll.options.criticalFailure;
    const fumbleTarget = attackRollFumbleTarget === 0 || midiFumble === 0 && attackRollFumbleTarget === 1 ? 0 : Math.max(attackRollFumbleTarget, midiFumble);
    const naturalFumble = critFumbleDiceRoll <= fumbleTarget;
    if (naturalFumble) {
      this.attackRollModifierTracker.fumble.setNatural("naturalFumble", `${i18n("DND5E.Roll")} <= ${fumbleTarget}`);
    }
    const actionType = this.activity?.actionType;
    const actorCriticalFlags = {
      criticalFlags: actionType ? ["critical.all", `critical.${actionType}`] : ["critical.all"],
      noCriticalFlags: actionType ? ["noCritical.all", `noCritical.${actionType}`] : ["noCritical.all"],
      fumbleFlags: actionType ? ["fumble.all", `fumble.${actionType}`] : ["fumble.all"],
      noFumbleFlags: actionType ? ["noFumble.all", `noFumble.${actionType}`] : ["noFumble.all"]
    };
    await computeCriticalModifiers(this.actor, actorCriticalFlags, this.attackRollModifierTracker, { workflow: this, actionType });
    if (this.targets.size >= 1) {
      const targetGrantsFumbleFlags = {
        criticalFlags: [],
        noCriticalFlags: [],
        fumbleFlags: actionType ? ["grants.fumble.all", `grants.fumble.${actionType}`] : ["grants.fumble.all"],
        noFumbleFlags: []
      };
      for (const target of this.targets) {
        if (target?.actor) {
          const targetName = target?.name ?? target?.actor?.name ?? "";
          await computeTargetCriticalModifiers(target.actor, targetName, targetGrantsFumbleFlags, this.attackRollModifierTracker, { workflow: this, grantsActor: this.actor });
          if (this.attackRollModifierTracker.fumble.isForced) break;
        }
      }
      if (naturalFumble || this.attackRollModifierTracker.fumble.isForced) {
        let allTargetsGrantNoFumble = true;
        const noFumbleTargets = [];
        let noFumbleFlag = "grants.noFumble.all";
        for (const target of this.targets) {
          if (!target?.actor) {
            allTargetsGrantNoFumble = false;
            break;
          }
          const targetName = target?.name ?? target?.actor?.name ?? "";
          const conditionData = createConditionData({ workflow: this, target, actor: this.actor });
          const hasNoFumbleAll = await evalAllConditionsAsync(target.actor, midiFlag2(`grants.noFumble.all`), conditionData);
          const hasNoFumbleType = actionType && await evalAllConditionsAsync(target.actor, midiFlag2(`grants.noFumble.${actionType}`), conditionData);
          if (!hasNoFumbleAll && !hasNoFumbleType) {
            allTargetsGrantNoFumble = false;
            break;
          }
          if (hasNoFumbleType) noFumbleFlag = `grants.noFumble.${actionType}`;
          noFumbleTargets.push(targetName);
        }
        if (allTargetsGrantNoFumble && this.targets.size > 0) {
          this.attackRollModifierTracker.fumble.suppress(noFumbleFlag, `${getFlagDisplayName(noFumbleFlag)} (${noFumbleTargets.join(", ")})`);
        }
      }
    }
    this.persistFields({ isCritical: this.isCritical, isFumble: this.isFumble });
    this.persistAttackTracker();
    this.attackTotal = this.attackRoll.total ?? 0;
    if (debugEnabled > 1) debug("processAttackRoll: ", this.diceRoll, this.attackTotal, this.isCritical, this.isFumble);
    if (this.isCritical || this.isFumble) {
      addAttribution(this.attackRoll, this.attackAttribution);
      this.attackRollHTML = await midiRenderAttackRoll(this.attackRoll);
    }
  }
  recordAttackStats() {
    if (!configSettings.keepRollStats || !this.item || !this.attackRoll) return;
    const ADV_MODE = CONFIG.Dice.D20Roll.ADV_MODE;
    gameStats.addAttackRoll({
      rawRoll: this.diceRoll,
      fumble: this.isFumble,
      critical: this.isCritical,
      total: this.attackTotal,
      advantageMode: this.attackRoll.options?.advantageMode ?? ADV_MODE.NORMAL
    }, this.item);
  }
  recordDamageStats() {
    if (!configSettings.keepRollStats || !this.item || !this.damageList?.length) return;
    const totalHpDamage = this.damageList.reduce((sum, dl) => sum + Math.max(0, dl.hpDamage ?? 0), 0);
    let totalDamage = 0;
    for (const dl of this.damageList) {
      if (dl.rawDamageDetail) {
        for (const dd of dl.rawDamageDetail) {
          if (dd.value > 0 && dd.type && !GameSystemConfig.healingTypes[dd.type]) {
            totalDamage += dd.value;
          }
        }
      }
    }
    let maxDamage = 0;
    let healingDone = 0;
    let kills = 0;
    for (const dl of this.damageList) {
      const hp = dl.hpDamage ?? 0;
      if (hp > 0) maxDamage = Math.max(maxDamage, hp);
      if (hp < 0) healingDone += Math.abs(hp);
      if ((dl.oldHP ?? 0) > 0 && (dl.newHP ?? 0) <= 0) kills += 1;
      if (hp !== 0 && dl.actorId) {
        const damageByType = {};
        if (hp > 0 && dl.damageDetail) {
          for (const dd of dl.damageDetail) {
            if (dd.value > 0 && dd.type) {
              damageByType[dd.type] = (damageByType[dd.type] ?? 0) + dd.value;
            }
          }
        }
        gameStats.addDamageTaken(dl.actorId, hp, damageByType);
      }
    }
    gameStats.addDamage(totalHpDamage, totalDamage, this.item, maxDamage, healingDone, kills, this.isCritical);
    const dealtByType = {};
    const appliedByType = {};
    for (const dl of this.damageList) {
      if (dl.rawDamageDetail) {
        for (const dd of dl.rawDamageDetail) {
          if (dd.value > 0 && dd.type && !GameSystemConfig.healingTypes[dd.type]) {
            dealtByType[dd.type] = (dealtByType[dd.type] ?? 0) + dd.value;
          }
        }
      }
      if (dl.damageDetail) {
        for (const dd of dl.damageDetail) {
          if (dd.value > 0 && dd.type && !GameSystemConfig.healingTypes[dd.type]) {
            appliedByType[dd.type] = (appliedByType[dd.type] ?? 0) + dd.value;
          }
        }
      }
    }
    if (Object.keys(dealtByType).length > 0 || Object.keys(appliedByType).length > 0) {
      gameStats.addDamageDealtByType(this.item, dealtByType, appliedByType);
    }
  }
  // Builds a [optionalKeys, flagOwners] tuple for the rolling actor + item enchantments.
  // flagOwners maps full flag paths (e.g. "flags.midi-qol.optional.dragonSlayer") to the item
  // when an enchantment on the rolling item is the source of that key. Actor keys take precedence.
  collectOptionalFlagSources() {
    const actorOptional = this.actor.flags?.[MODULE_ID]?.optional ?? {};
    const itemOptional = this.item?.flags?.[MODULE_ID]?.optional ?? {};
    const keys = Object.keys(actorOptional);
    const owners = {};
    for (const key of Object.keys(itemOptional)) {
      if (!(key in actorOptional)) {
        keys.push(key);
        owners[`flags.${MODULE_ID}.optional.${key}`] = this.item;
      }
    }
    return { keys, owners };
  }
  async processAttackRollBonusFlags() {
    let attackBonus = "attack.all";
    if (this.activity && this.activity.hasAttack) attackBonus = `attack.${this.activity.actionType}`;
    const { keys: allOptionalKeys, owners: flagOwners } = this.collectOptionalFlagSources();
    const ownerOfShort = /* @__PURE__ */ __name((flag) => flagOwners[`flags.${MODULE_ID}.optional.${flag}`] ?? this.actor, "ownerOfShort");
    let bonusFlags = allOptionalKeys.filter((flag) => {
      const owner = ownerOfShort(flag);
      const hasAttackFlag = foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}.attack.all`)) || foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}.${attackBonus}`));
      if (hasAttackFlag === void 0) return false;
      if (this.isFumble && !hasAttackFlag?.includes("roll")) return false;
      const flagData = foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}`));
      if (!flagData?.count) return true;
      return getOptionalCountRemainingShortFlag(this.actor, flag, owner) > 0;
    }).map((flag) => midiFlag2(`optional.${flag}`));
    if (bonusFlags.length > 0) {
      const displayBonusRolls = bonusFlags.reduce((acc, flag) => acc || foundry.utils.getProperty(flagOwners[flag] ?? this.actor ?? {}, `${flag}.displayBonusRolls`), void 0) ?? checkMechanic("displayBonusRolls");
      const newRoll = await bonusDialog.bind(this)(bonusFlags, attackBonus, displayBonusRolls, `${this.actor.name} - ${i18n("DND5E.Attack")} ${i18n("DND5E.Roll")}`, this.attackRoll, "attackRoll", { flagOwners });
      if (newRoll) this.setAttackRoll(newRoll);
    }
    if (this.targets.size === 1) {
      const target = this.targets.first();
      const descriptor = getTargetDescriptor(target.actor?.uuid ?? "", this.targetDescriptors);
      const targetAC = descriptor?.ac ?? target.actor?.system.attributes.ac.value;
      await this.processAttackRoll();
      const isMiss = this.isFumble || (this.attackRoll.total ?? 0) < targetAC;
      if (isMiss) {
        attackBonus = "attack.fail.all";
        if (this.activity && this.activity.hasAttack) attackBonus = `attack.fail.${this.activity.actionType}`;
        let bonusFlags2 = allOptionalKeys.filter((flag) => {
          const owner = ownerOfShort(flag);
          const hasAttackFlag = foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}.attack.fail.all`)) || foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}.${attackBonus}`));
          if (hasAttackFlag === void 0) return false;
          if (this.isFumble && !hasAttackFlag?.includes("roll")) return false;
          const flagData = foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}`));
          if (!flagData?.count) return true;
          return getOptionalCountRemainingShortFlag(this.actor, flag, owner) > 0;
        }).map((flag) => midiFlag2(`optional.${flag}`));
        if (bonusFlags2.length > 0) {
          const displayBonusRolls = bonusFlags2.reduce((acc, flag) => acc || foundry.utils.getProperty(flagOwners[flag] ?? this.actor ?? {}, `${flag}.displayBonusRolls`), void 0) ?? checkMechanic("displayBonusRolls");
          const newRoll = await bonusDialog.bind(this)(bonusFlags2, attackBonus, displayBonusRolls, `${this.actor.name} - ${i18n("DND5E.Attack")} ${i18n("DND5E.Roll")}`, this.attackRoll, "attackRoll", { flagOwners });
          if (newRoll) this.setAttackRoll(newRoll);
        }
      }
    }
    return this.attackRoll;
  }
  async processDamageRollBonusFlags(damageRolls) {
    let damageBonus = "damage.all";
    const rollMode = safeGetGameSetting("core", "rollMode");
    const hideDSN = game.user?.isGM && [CONST.DICE_ROLL_MODES.SELF, CONST.DICE_ROLL_MODES.BLIND, CONST.DICE_ROLL_MODES.PRIVATE].includes(rollMode);
    if (this.activity) damageBonus = `damage.${this.activity.actionType}`;
    const { keys: allOptionalKeys, owners: flagOwners } = this.collectOptionalFlagSources();
    const ownerOfShort = /* @__PURE__ */ __name((flag) => flagOwners[`flags.${MODULE_ID}.optional.${flag}`] ?? this.actor, "ownerOfShort");
    const bonusFlags = allOptionalKeys.filter((flag) => {
      const owner = ownerOfShort(flag);
      const hasDamageFlag = foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}.damage.all`)) !== void 0 || foundry.utils.getProperty(owner ?? {}, midiFlag2(`optional.${flag}.${damageBonus}`)) !== void 0;
      if (!hasDamageFlag) return false;
      return getOptionalCountRemainingShortFlag(this.actor, flag, owner) > 0;
    }).map((flag) => midiFlag2(`optional.${flag}`));
    if (bonusFlags.length > 0) {
      const displayBonusRolls = bonusFlags.reduce((acc, flag) => acc || foundry.utils.getProperty(flagOwners[flag] ?? this.actor ?? {}, `${flag}.displayBonusRolls`), void 0) ?? checkMechanic("displayBonusRolls");
      let newRoll = await bonusDialog.bind(this)(bonusFlags, damageBonus, displayBonusRolls === true, `${this.actor.name} - ${i18n("DND5E.Damage")} ${i18n("DND5E.Roll")}`, damageRolls[0], "damageRoll", { hideDSN, damageRolls, flagOwners });
      if (newRoll) damageRolls[0] = newRoll;
    }
    return damageRolls;
  }
  recordTargetACModifiers() {
    this.targetACModifiers = /* @__PURE__ */ new Map();
    const allTargetDescriptors = this.targetDescriptors;
    for (let targetToken of this.targets) {
      let targetActor = targetToken.actor;
      if (!targetActor) continue;
      const targetDescriptor = getTargetDescriptor(targetActor.uuid, allTargetDescriptors);
      if (targetDescriptor?.ac != null) {
        this.targetACModifiers.set(targetActor.uuid, targetDescriptor.ac - targetActor.system.attributes.ac.value);
      }
    }
  }
  async checkHits(options = {}) {
    let isHit = true;
    let isHitEC = false;
    if (this.activity.target?.affects?.type === "self") {
      this.targets = await getOrCreateTokenForActorAsSet(this.actor);
      this.hitTargets = new Set(this.targets);
    } else this.hitTargets = /* @__PURE__ */ new Set();
    this.hitDisplayData = {};
    const challengeModeArmorSet = ![void 0, false, "none"].includes(checkRule("challengeModeArmor"));
    for (let targetToken of this.targets) {
      let targetName = configSettings.useTokenNames && targetToken.name ? targetToken.name : targetToken.actor?.name;
      let targetActor = targetToken.actor;
      if (!targetActor) continue;
      let targetACMod = this.targetACModifiers.get(targetActor.uuid) ?? 0;
      let attackBonus = 0;
      let vehicleACOverride;
      const wjVehicle = installedModules.get("wjmais") ? foundry.utils.getProperty(targetActor, "flags.wjmais.crew.min") != null : false;
      if (targetActor.type === "vehicle" && !wjVehicle) {
        const inMotion = targetActor.flags?.[MODULE_ID]?.inMotion;
        if (inMotion) vehicleACOverride = Number.parseInt(targetActor.system.attributes.ac.flat ?? 10);
        else vehicleACOverride = Number.parseInt(targetActor.system.attributes.ac.motionless);
        if (isNaN(vehicleACOverride)) {
          console.warn("Error when getting vehicle armor class make sure motionless is set");
          vehicleACOverride = 10;
        }
      }
      let hitResultNumeric;
      let targetEC = (targetActor.system.attributes.ac.EC ?? 0) + targetACMod;
      let targetAR = (targetActor.system.attributes.ac.AR ?? 0) + targetACMod;
      isHit = false;
      isHitEC = false;
      let attackTotal = this.attackTotal;
      const noCoverFlag = this.actor.flags?.[MODULE_ID]?.ignoreCover;
      let ignoreCover = false;
      if (noCoverFlag) {
        const conditionData2 = createConditionData({ workflow: this, target: targetToken, actor: this.actor });
        ignoreCover ||= await evalAllConditionsAsync(this.actor, midiFlag2(`ignoreCover`), conditionData2);
      }
      if (this.activity.actionType === "rsak" && this.actor.flags?.dnd5e?.spellSniper) {
        const conditionData2 = createConditionData({ workflow: this, target: targetToken, actor: this.actor });
        ignoreCover ||= await evalAllConditionsAsync(this.actor, `flags.dnd5e.spellSniper`, conditionData2);
      }
      const sharpShooterFlag = this.actor.flags?.[MODULE_ID]?.sharpShooter || this.actor.flags?.dnd5e?.sharpShooter;
      if (this.activity.actionType === "rwak" && sharpShooterFlag) {
        const conditionData2 = createConditionData({ workflow: this, target: targetToken, actor: this.actor });
        ignoreCover ||= await evalAllConditionsAsync(this.actor, midiFlag2(`sharpShooter`), conditionData2);
        ignoreCover ||= await evalAllConditionsAsync(this.actor, `flags.dnd5e.sharpShooter`, conditionData2);
      }
      let flankingACMod = 0;
      const flankingBonusSetting = configSettings.optionalRules.flankingBonus ?? "advantage";
      if (flankingBonusSetting !== "advantage") {
        const allDescriptors = this.targetDescriptors;
        const targetFlankDescriptor = getTargetDescriptor(targetActor.uuid, allDescriptors);
        flankingACMod = targetFlankDescriptor?.flankingBonus ?? 0;
      }
      let acResult = this.token ? computeTargetAC(this.token, targetToken, this.activity, { ignoreCover, flankingMod: flankingACMod, targetACMod }) : void 0;
      let targetAC = vehicleACOverride ?? acResult?.effectiveAC ?? Number.parseInt(targetActor.system.attributes.ac.value ?? 10) + targetACMod;
      let bonusAC = acResult?.effectiveCoverBonus ?? 0;
      const midiFlagsAttackBonus = targetActor.flags?.[MODULE_ID]?.grants?.attack?.bonus;
      let reactionACDelta = 0;
      let reactionItemName;
      if (!this.isFumble) {
        if (midiFlagsAttackBonus) {
          if (midiFlagsAttackBonus?.all) {
            const bonusValue = midiFlagsAttackBonus.all;
            attackBonus = Number.isNumeric(bonusValue) ? Number(bonusValue) : (await new Roll(`${bonusValue}`, targetActor.getRollData()).evaluate()).total ?? 0;
            foundry.utils.setProperty(this.actor, "flags.midi.evaluated.grants.attack.bonus.all", { value: attackBonus, effects: [`${targetActor.name}`] });
          }
          if (midiFlagsAttackBonus[this.activity.actionType]) {
            const bonusValue = midiFlagsAttackBonus[this.activity.actionType];
            attackBonus = Number.isNumeric(bonusValue) ? Number(bonusValue) : (await new Roll(`${bonusValue}`, targetActor.getRollData()).evaluate()).total ?? 0;
            foundry.utils.setProperty(this.actor, `flags.midi.evaluated.grants.attack.bonus.${this.activity.actionType}`, { value: attackBonus, effects: [`${targetActor.name}`] });
          }
        }
        if (challengeModeArmorSet) isHit = attackTotal > targetAC || this.isCritical;
        else {
          if (this.attackRoll && !this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
            const workflowOptions = foundry.utils.mergeObject(this.workflowOptions, { sourceActorUuid: this.actor.uuid, sourceItemUuid: this.item.uuid }, { inplace: false, overwrite: true });
            const result = await doReactions(targetToken, this.tokenUuid, this.attackRoll, "reactionattacked", { activity: this.activity, item: this.item, workflow: this, workflowOptions });
            if (result?.ac && isFinite(targetAC)) {
              const preReactionRawAC = targetToken.actor?.system.attributes.ac?.value ?? 10;
              reactionACDelta = result.ac - preReactionRawAC;
              reactionItemName = result.name;
              targetAC = result.ac + (acResult?.appliedCoverDelta ?? 0) + targetACMod - flankingACMod;
            } else if (result?.uuid && isFinite(targetAC)) {
              const postReactionACResult = this.token ? computeTargetAC(this.token, targetToken, this.activity, { ignoreCover, flankingMod: flankingACMod, targetACMod }) : void 0;
              if (postReactionACResult && postReactionACResult.effectiveAC !== targetAC) {
                const preReactionBaseAC = acResult?.baseAC ?? postReactionACResult.baseAC;
                reactionACDelta = postReactionACResult.baseAC - preReactionBaseAC;
                postReactionACResult.baseAC = preReactionBaseAC;
                acResult = postReactionACResult;
                targetAC = acResult.effectiveAC;
                bonusAC = acResult.effectiveCoverBonus;
                reactionItemName = result.itemName ?? result.name;
              }
            }
          }
          isHit = attackTotal + attackBonus >= targetAC || this.isCritical;
        }
        if (!isFinite(targetAC)) isHit = false;
        if (targetEC) isHitEC = challengeModeArmorSet && attackTotal <= targetAC && attackTotal >= targetEC && isFinite(targetAC);
        if ((isHit || isHitEC) && this.activity.attack && this.attackRoll && targetToken !== null && !foundry.utils.getProperty(this, `item.flags.${MODULE_ID}.noProvokeReaction`) && !this.workflowOptions.noProvokeReaction) {
          const workflowOptions = foundry.utils.mergeObject(this.workflowOptions, { sourceActorUuid: this.actor.uuid, sourceItemUuid: this.item.uuid }, { inplace: false, overwrite: true });
          let result;
          if (!this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
            result = await doReactions(targetToken, this.tokenUuid, this.attackRoll, "reaction", { activity: this.activity, item: this.item, workflow: this, workflowOptions });
          }
          if (!_Workflow.getWorkflow(this.id))
            return;
          if (result?.ac) {
            const preReactionRawAC = targetToken.actor?.system.attributes.ac?.value ?? 10;
            reactionACDelta = result.ac - preReactionRawAC;
            reactionItemName = result.name;
            targetAC = result.ac + (acResult?.appliedCoverDelta ?? 0) - flankingACMod;
          } else if (result?.uuid) {
            const postReactionACResult = this.token ? computeTargetAC(this.token, targetToken, this.activity, { ignoreCover, flankingMod: flankingACMod, targetACMod }) : void 0;
            if (postReactionACResult && postReactionACResult.effectiveAC !== targetAC) {
              const preReactionBaseAC = acResult?.baseAC ?? postReactionACResult.baseAC;
              reactionACDelta = postReactionACResult.baseAC - preReactionBaseAC;
              postReactionACResult.baseAC = preReactionBaseAC;
              acResult = postReactionACResult;
              targetAC = acResult.effectiveAC;
              bonusAC = acResult.effectiveCoverBonus;
              reactionItemName = result.itemName ?? result.name;
            }
          }
          if (targetEC) targetEC = targetAC - targetAR + targetACMod;
          isHit = (attackTotal + attackBonus >= targetAC || this.isCritical) && result?.name !== "missed";
          if (challengeModeArmorSet) isHit = this.attackTotal >= targetAC || this.isCritical;
          if (targetEC) isHitEC = challengeModeArmorSet && this.attackTotal <= targetAC && this.attackTotal >= targetEC && isFinite(targetAC);
        } else if (!isHit && !isHitEC && this.activity.attack && this.attackRoll && targetToken !== null && !foundry.utils.getProperty(this, `item.flags.${MODULE_ID}.noProvokeReaction`)) {
          const workflowOptions = foundry.utils.mergeObject(this.workflowOptions, { sourceActorUuid: this.actor.uuid, sourceItemUuid: this.item.uuid }, { inplace: false, overwrite: true });
          if (!this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
            await doReactions(targetToken, this.tokenUuid, this.attackRoll, "reactionmissed", { activity: this.activity, item: this.item, workflow: this, workflowOptions });
          }
        }
        const optionalCrits = checkRule("optionalCritRule");
        if (this.targets.size === 1 && configSettings.optionalRulesEnabled && optionalCrits > -1) {
          if (checkRule("criticalNat20") && this.isCritical) {
          } else {
            const optionalCrit = attackTotal >= targetAC + optionalCrits;
            this.attackRollModifierTracker.critical.setOverride(optionalCrit, "optionalCritRule", i18n("midi-qol.OptionalCritRule"));
          }
        }
        hitResultNumeric = this.isCritical ? "++" : !isFinite(targetAC) ? `${attackTotal}/\u221E` : `${attackTotal}/${Math.abs(attackTotal - targetAC)}`;
      }
      const midiFlagsActorFail = this.actor.flags?.[MODULE_ID]?.fail;
      if (midiFlagsActorFail) {
        const conditionData2 = createConditionData({ workflow: this, target: targetToken, actor: this.actor });
        let failFlag;
        if (await evalAllConditionsAsync(this.actor, midiFlag2(`fail.attack.${this.activity.actionType}`), conditionData2))
          failFlag = `fail.attack.${this.activity.actionType}`;
        else if (await evalAllConditionsAsync(this.actor, midiFlag2(`fail.attack.all`), conditionData2))
          failFlag = "fail.attack.all";
        else if (await evalAllConditionsAsync(this.actor, midiFlag2(`fail.all`), conditionData2))
          failFlag = "fail.all";
        if (failFlag) {
          isHit = false;
          isHitEC = false;
          this.attackRollModifierTracker.critical.setOverride(false, failFlag, getFlagDisplayName(failFlag));
        }
      }
      const midiFlagsActorSuccess = this.actor.flags?.[MODULE_ID]?.success;
      if (midiFlagsActorSuccess) {
        const conditionData2 = createConditionData({ workflow: this, target: targetToken, actor: this.actor });
        let successFlag;
        if (await evalAllConditionsAsync(this.actor, midiFlag2(`success.attack.${this.activity.actionType}`), conditionData2))
          successFlag = `success.attack.${this.activity.actionType}`;
        else if (await evalAllConditionsAsync(this.actor, midiFlag2(`success.attack.all`), conditionData2))
          successFlag = "success.attack.all";
        else if (await evalAllConditionsAsync(this.actor, midiFlag2(`success.all`), conditionData2))
          successFlag = "success.all";
        if (successFlag) {
          isHit = true;
          isHitEC = false;
          this.attackRollModifierTracker.fumble.setOverride(false, successFlag, getFlagDisplayName(successFlag));
        }
      }
      const midiFlagsGrantsAttackSuccess = targetActor.flags?.[MODULE_ID]?.grants?.attack?.success;
      const midiFlagsGrantsAttackFail = targetActor.flags?.[MODULE_ID]?.grants?.attack?.fail;
      let conditionData;
      if (midiFlagsGrantsAttackSuccess || midiFlagsGrantsAttackFail) {
        conditionData = createConditionData({ workflow: this, target: targetToken, actor: this.actor });
        let grantsSuccessFlag;
        if (await evalAllConditionsAsync(targetActor, midiFlag2(`grants.attack.success.${this.activity.actionType}`), conditionData))
          grantsSuccessFlag = `grants.attack.success.${this.activity.actionType}`;
        else if (await evalAllConditionsAsync(targetActor, midiFlag2(`grants.attack.success.all`), conditionData))
          grantsSuccessFlag = "grants.attack.success.all";
        if (grantsSuccessFlag) {
          isHit = true;
          isHitEC = false;
          this.attackRollModifierTracker.fumble.setOverride(false, grantsSuccessFlag, getFlagDisplayName(grantsSuccessFlag));
        }
        let grantsFailFlag;
        if (await evalAllConditionsAsync(targetActor, midiFlag2(`grants.attack.fail.${this.activity.actionType}`), conditionData))
          grantsFailFlag = `grants.attack.fail.${this.activity.actionType}`;
        else if (await evalAllConditionsAsync(targetActor, midiFlag2(`grants.attack.fail.all`), conditionData))
          grantsFailFlag = "grants.attack.fail.all";
        if (grantsFailFlag) {
          isHit = false;
          isHitEC = false;
          this.attackRollModifierTracker.critical.setOverride(false, grantsFailFlag, getFlagDisplayName(grantsFailFlag));
        }
      }
      let hitScale = 1;
      if (["scale", "scaleNoAR"].includes(checkRule("challengeModeArmor")) && !this.isCritical) hitScale = Math.floor((this.attackTotal - targetEC + 1) / ((targetActor?.system.attributes.ac.AR ?? 0) + 1) * 10) / 10;
      if (!this.challengeModeScale) this.challengeModeScale = {};
      this.challengeModeScale[targetToken.actor?.uuid ?? "dummy"] = hitScale;
      if (this.isCritical && isFinite(targetAC)) isHit = true;
      if (isHit || this.isCritical && isFinite(targetAC)) this.hitTargets = this.hitTargets.add(targetToken);
      if (isHitEC) this.hitTargetsEC = this.hitTargetsEC.add(targetToken);
      if (isHit || isHitEC) await this.processCriticalFlags();
      if (game.user?.isGM) log(`${this.speaker?.alias} Rolled a ${this.attackTotal + attackBonus} to hit ${targetName} 's AC of ${targetAC} ${isHit ? "hitting" : "missing"}`);
      let attackType = "";
      let hitSymbol = "fa-blank";
      let isHitResult = "miss";
      if (game.user?.isGM && ["hitMiss", "hidden"].includes(configSettings.gmAttackDisplay ?? "full") && (this.isCritical || isHit || isHitEC)) {
        isHitResult = "hit";
        hitSymbol = "fa-tick";
      } else if (this.isCritical) {
        isHitResult = "critical";
        hitSymbol = "fa-check-double";
      } else if (game.user?.isGM && this.isFumble && ["hitMiss", "hidden"].includes(configSettings.gmAttackDisplay ?? "full")) {
        isHitResult = "miss";
        hitSymbol = "fa-xmark";
      } else if (this.isFumble) {
        isHitResult = "fumble";
        hitSymbol = "fa-xmark";
      } else if (isHit) {
        isHitResult = "hit";
        hitSymbol = "fa-check";
      } else if (isHitEC && ["scale", "scaleNoAR"].includes(checkRule("challengeModeArmor"))) {
        isHitResult = "hitEC";
        hitSymbol = "fa-check";
      } else if (isHitEC) {
        isHitResult = "hitEC";
        hitSymbol = "fa-check";
      } else {
        isHitResult = "miss";
        hitSymbol = "fa-xmark";
      }
      let hitStyle = "";
      let img = targetToken.document.texture.src || targetToken.actor?.img || "";
      if (configSettings.usePlayerPortrait && targetToken.actor?.type === "character") {
        img = targetToken.actor?.img || targetToken.document.texture.src || "";
      }
      if (foundry.helpers.media.VideoHelper.hasVideoExtension(img ?? "")) {
        img = await game.video?.createThumbnail(img ?? "", { width: 100, height: 100 }) ?? "";
      }
      if (this.isFumble) hitResultNumeric = "--";
      const targetUuid = targetToken.document.uuid;
      const effectiveAC = targetAC - attackBonus;
      const acDisplay = !isFinite(targetAC) ? "\u221E" : `${effectiveAC}`;
      const acIcons = [];
      let acTooltipParts = `${acResult?.baseAC ?? effectiveAC}`;
      if (targetACMod) acTooltipParts += ` ${targetACMod > 0 ? "+" : ""}${targetACMod}`;
      const displayCover = acResult?.effectiveCoverBonus ?? 0;
      if (!acResult?.coverIgnored && displayCover >= TOTAL_COVER) {
        acIcons.push({ img: "systems/dnd5e/icons/svg/statuses/cover-total.svg", tooltip: `+${displayCover}: ${i18n("EFFECT.DND5E.StatusTotalCover")}`, cssClass: "midi-qol-ac-icon", useDnd5eIcon: true });
      } else if (!acResult?.coverIgnored && displayCover >= THREE_QUARTERS_COVER) {
        acIcons.push({ img: "systems/dnd5e/icons/svg/statuses/cover-three-quarters.svg", tooltip: `+${displayCover}: ${i18n("EFFECT.DND5E.StatusThreeQuartersCover")}`, cssClass: "midi-qol-ac-icon", useDnd5eIcon: true });
      } else if (!acResult?.coverIgnored && displayCover >= HALF_COVER) {
        acIcons.push({ img: "systems/dnd5e/icons/svg/statuses/cover-half.svg", tooltip: `+${displayCover}: ${i18n("EFFECT.DND5E.StatusHalfCover")}`, cssClass: "midi-qol-ac-icon", useDnd5eIcon: true });
      }
      if (!acResult?.coverIgnored && acResult && acResult.effectiveCoverBonus > 0)
        acTooltipParts += ` +${acResult.effectiveCoverBonus}`;
      if (acResult?.coverIgnored) {
        if (this.activity?.actionType === "rwak") {
          acIcons.push({ img: "icons/skills/targeting/target-strike-gray.webp", tooltip: i18n("midi-qol.SharpShooter"), cssClass: "midi-qol-ac-icon-img" });
        } else if (this.activity?.actionType === "rsak") {
          acIcons.push({ img: "icons/skills/targeting/crosshair-triple-strike-orange.webp", tooltip: i18n("midi-qol.SpellSniper"), cssClass: "midi-qol-ac-icon-img" });
        }
      }
      const isMeleeAction = ["mwak", "msak"].includes(this.activity?.actionType ?? "");
      if (flankingACMod > 0 && isMeleeAction) {
        const flankingMode = checkRule("checkFlanking");
        const isFlankedMode = ["ceflanked", "ceflankedNoconga", "midiFlanked", "midiFlankedNoConga"].includes(flankingMode);
        acIcons.push({
          img: isFlankedMode ? "modules/midi-qol/icons/encirclement.svg" : "icons/svg/sword.svg",
          tooltip: `-${flankingACMod}: ${isFlankedMode ? i18n("midi-qol.Flanked") : i18n("midi-qol.Flanking")}`,
          cssClass: "midi-qol-ac-icon-img"
        });
        acTooltipParts += ` -${flankingACMod}`;
      }
      if (reactionACDelta) {
        acIcons.push({ img: "modules/midi-qol/icons/reaction.svg", tooltip: `${reactionACDelta > 0 ? "+" : ""}${reactionACDelta}: ${i18n("DND5E.Reaction")}: ${reactionItemName}`, cssClass: "midi-qol-ac-icon", useDnd5eIcon: true });
        acTooltipParts += ` ${reactionACDelta > 0 ? "+" : ""}${reactionACDelta}`;
      }
      if (attackBonus) {
        const grantsFlagKey = `flags.${MODULE_ID}.grants.attack.bonus`;
        const grantsEffectNames = targetActor.allApplicableEffects().filter((e) => !e.disabled && e.changes?.some((c) => c.key.startsWith(grantsFlagKey))).map((e) => e.name).toArray();
        if (grantsEffectNames.length) {
          const acDelta = -attackBonus;
          acIcons.push({ icon: "fas fa-bolt", tooltip: `${acDelta > 0 ? "+" : ""}${acDelta}: ${grantsEffectNames.join(", ")}`, cssClass: "midi-qol-ac-icon-fa" });
        }
        acTooltipParts += ` ${-attackBonus > 0 ? "+" : ""}${-attackBonus}`;
      }
      let acTooltip;
      if (isFinite(targetAC) && acTooltipParts !== `${effectiveAC}`) {
        acTooltip = `${effectiveAC} = ${acTooltipParts}`;
      }
      this.hitDisplayData[targetUuid] = {
        targetClass: !!targetToken.actor?.hasPlayerOwner ? "midi-qol-player-target" : "midi-qol-npc-target",
        target: targetToken,
        actorUuid: targetToken.actor?.uuid,
        uuid: targetUuid,
        hitStyle,
        attackBonus,
        ac: effectiveAC,
        acDisplay,
        acTooltip,
        //@ts-expect-error no dnd5e-types
        baseAc: targetToken.actor?.system.attributes.ac?.value ?? 10,
        hitClass: ["hit", "critical", "isHitEC"].includes(isHitResult) ? "success" : "failure",
        acClass: targetToken.actor.hasPlayerOwner ? "" : "midi-qol-npc-ac",
        acIcons,
        hitSymbol,
        attackType,
        showAC: true,
        img,
        gmName: getTokenName(targetToken),
        playerName: getTokenPlayerName(targetToken.document) ?? "",
        hitResultNumeric,
        attackTotal,
        isHit: ["hit", "critical", "isHitEC"].includes(isHitResult)
      };
    }
    if (configSettings.allowUseMacro && !options.noTargetOnUseMacro) await this.triggerTargetMacros(["isHit"], /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]));
    if (configSettings.allowUseMacro && !options.noTargetOnUseMacro) await this.triggerTargetMacros(["isMissed"], this.targets.difference(this.hitTargets).difference(this.hitTargetsEC));
    this.hitTargets = this.hitTargets;
    this.hitTargetsEC = this.hitTargetsEC;
    this.persistAttackTracker();
  }
  async setAttackRoll(roll) {
    this.attackRoll = roll;
    foundry.utils.setProperty(roll, "options.midi-qol.rollType", "attack");
    this.attackTotal = roll.total ?? 0;
    this.attackRollHTML = await midiRenderAttackRoll(roll);
  }
  async setUtilityRolls(rolls) {
    if (!rolls) {
      this.utilityRolls = void 0;
      return;
    }
    ;
    if (rolls instanceof Roll) rolls = [rolls];
    this.utilityRolls = rolls;
    this.utilityRollHTML = "";
    for (let roll of this.utilityRolls) {
      foundry.utils.setProperty(roll, `options.${MODULE_ID}.rollType`, "utility");
      this.utilityRollHTML += await midiRenderFormulaRoll(roll);
    }
  }
  setUtilityRoll(roll) {
    return this.setUtilityRolls([roll]);
  }
  convertRollToDamageRoll(roll) {
    const DamageRoll = CONFIG.Dice.DamageRoll;
    if (!(roll instanceof DamageRoll)) {
      let damageType = MidiQOL.MQdefaultDamageType;
      if (roll.terms[0].options.flavor && getDamageType(roll.terms[0].options.flavor)) {
        damageType = getDamageType(roll.terms[0].options.flavor);
      } else if (this.activity.hasDamage) damageType = this.activity.damage.parts[0]?.types.first();
      else if (this.activity.hasHealing) damageType = this.activity.healing.types.first() ?? "healing";
      const message = `convertRollToDamageRoll: roll is not a damage roll, setting type to ${damageType}`;
      TroubleShooter.recordError(new Error(message));
      const damageRoll = new DamageRoll(roll.formula, roll.data, roll.options);
      Object.assign(damageRoll, roll);
      damageRoll.options.type = damageType;
      error2("convertRollToDamageRoll: roll is not a damage roll", damageRoll);
      return damageRoll;
    }
    return roll;
  }
  async setDamageRoll(roll) {
    this.setDamageRolls([this.convertRollToDamageRoll(roll)]);
  }
  async addDamageRolls(rolls) {
    if (!rolls) return;
    if (rolls instanceof Roll) rolls = [rolls];
    if (!this.damageRolls) this.damageRolls = [];
    this.damageRolls = this.damageRolls.concat(rolls.map((roll) => this.convertRollToDamageRoll(roll)));
    this.damageTotal = sumRolls(this.damageRolls, "positive");
    this.healingAdjustedDamageTotal = sumRolls(this.damageRolls, "negativeIgnoreTemp");
    this.damageRollHTML = "";
    for (let roll of this.damageRolls) {
      foundry.utils.setProperty(roll, `options.${MODULE_ID}.rollType`, "defaultDamage");
      foundry.utils.setProperty(roll, "options.flavor", "");
      this.damageRollHTML += await midiRenderDamageRoll(roll);
    }
    this.rawDamageDetail = createDamageDetail({ roll: this.damageRolls, activity: this.activity, defaultType: this.defaultDamageType });
    this.damageDetail = foundry.utils.deepClone(this.rawDamageDetail);
    return;
  }
  async setDamageRolls(rolls) {
    if (!rolls) {
      this.damageRolls = [];
      return;
    }
    ;
    if (rolls instanceof Roll) rolls = [rolls];
    const baseRollProperties = rolls[0]?.options?.properties ?? [];
    rolls.forEach((roll) => {
      setRollOperatorEvaluated(roll);
    });
    for (let i = 0; i < rolls.length; i++) {
      rolls[i] = await rolls[i];
      if (!rolls[i]._evaluated) rolls[i] = await rolls[i].evaluate();
    }
    for (let i = 1; i < rolls.length; i++) {
      foundry.utils.setProperty(rolls[i], "options.properties", (rolls[i].options?.properties ?? []).concat(baseRollProperties));
    }
    this.damageRolls = rolls;
    this.damageTotal = sumRolls(this.damageRolls, "positive");
    this.healingAdjustedDamageTotal = sumRolls(this.damageRolls, "negativeIgnoreTemp");
    this.damageRollHTML = "";
    for (let roll of this.damageRolls) {
      foundry.utils.setProperty(roll, `options.${MODULE_ID}.rollType`, "defaultDamage");
      foundry.utils.setProperty(roll, "options.flavor", "");
      this.damageRollHTML += await midiRenderDamageRoll(roll);
    }
    this.flavor = i18n("DND5E.Damage");
    this.rawDamageDetail = createDamageDetail({ roll: this.damageRolls, activity: this.activity, defaultType: this.defaultDamageType });
    this.damageDetail = foundry.utils.deepClone(this.rawDamageDetail);
    return;
  }
  async setBonusDamageRolls(rolls) {
    if (!rolls) {
      this.bonusDamageRolls = void 0;
      this.rawBonusDamageDetail = [];
      return;
    }
    ;
    if (rolls instanceof Roll) rolls = [rolls];
    rolls.forEach((roll) => {
      setRollOperatorEvaluated(roll);
    });
    for (let i = 0; i < rolls.length; i++) {
      rolls[i] = await rolls[i];
      if (!rolls[i]._evaluated) rolls[i] = await rolls[i].evaluate();
    }
    this.bonusDamageRolls = rolls;
    this.bonusDamageTotal = sumRolls(this.bonusDamageRolls, "positive");
    this.healingAdjustedBonusDamageTotal = sumRolls(this.bonusDamageRolls, "negativeIgnoreTemp");
    this.bonusDamageRollHTML = "";
    for (let roll of this.bonusDamageRolls) {
      foundry.utils.setProperty(roll, `options.${MODULE_ID}.rollType`, "bonusDamage");
      this.bonusDamageRollHTML += await midiRenderBonusDamageRoll(roll);
    }
    this.rawBonusDamageDetail = createDamageDetail({ roll: this.bonusDamageRolls, activity: this.activity, defaultType: this.defaultDamageType });
    this.bonusDamageDetail = foundry.utils.deepClone(this.rawBonusDamageDetail);
    return;
  }
  async setBonusDamageRoll(roll) {
    this.setBonusDamageRolls([this.convertRollToDamageRoll(roll)]);
  }
  async setOtherDamageRolls(rolls) {
    if (!rolls) {
      this.otherDamageRolls = void 0;
      this.rawOtherDamageDetail = [];
      return;
    }
    ;
    if (rolls instanceof Roll) rolls = [rolls];
    rolls.forEach((roll) => {
      setRollOperatorEvaluated(roll);
    });
    for (let i = 0; i < rolls.length; i++) {
      rolls[i] = await rolls[i];
      if (!rolls[i]._evaluated) rolls[i] = await rolls[i].evaluate();
    }
    this.otherDamageRolls = rolls;
    this.otherDamageTotal = sumRolls(this.otherDamageRolls, "positive");
    this.healingAdjustedOtherDamageTotal = sumRolls(this.otherDamageRolls, "negativeIgnoreTemp");
    this.otherDamageRollHTML = "";
    for (let roll of this.otherDamageRolls) {
      foundry.utils.setProperty(roll, `options.${MODULE_ID}.rollType`, "otherDamage");
      this.otherDamageRollHTML += await midiRenderOtherDamageRoll(roll);
    }
    this.rawOtherDamageDetail = createDamageDetail({ roll: this.otherDamageRolls, activity: this.activity, defaultType: this.defaultDamageType });
    this.otherDamageDetail = foundry.utils.deepClone(this.rawOtherDamageDetail);
    return;
  }
  async setOtherDamageRoll(roll) {
    this.setOtherDamageRolls([this.convertRollToDamageRoll(roll)]);
  }
  async chooseEffects(effects, options) {
    if (effects.length === 0) {
      if (debugEnabled > 0)
        warn(`chooseEffect | no effects found for ${this.item.name}`);
      return [];
    }
    const { DialogV2: DialogV26 } = foundry.applications.api;
    const buttons = effects.map((effect) => ({
      action: effect.uuid,
      label: effect.name,
      callback: /* @__PURE__ */ __name(() => effect, "callback")
    }));
    const chosen = await DialogV26.wait({
      window: { title: `${i18n("CONTROLS.CommonSelect")} ${i18n("DOCUMENT.ActiveEffect")}` },
      classes: ["midi-qol-choose-effect"],
      position: { width: 220 },
      buttons,
      render: /* @__PURE__ */ __name((_event, dialog) => {
        dialog.element.querySelectorAll(".form-footer button").forEach((btn) => {
          const eff = fromUuidSync(btn.dataset.action ?? "");
          if (!eff) return;
          const img = document.createElement("img");
          img.src = eff.img ?? "";
          const span = btn.querySelector("span");
          if (span) span.dataset.tooltip = eff.name;
          btn.prepend(img);
        });
      }, "render")
    });
    if (!chosen) return [];
    return [chosen];
  }
};
var SavesFirstWorkflow = class extends Workflow {
  static {
    __name(this, "SavesFirstWorkflow");
  }
  get postAttackRollState() {
    return this.WorkflowState_WaitForSaves;
  }
  async WorkflowState_WaitForSaves(context = {}) {
    await super.WorkflowState_WaitForSaves(context);
    return this.WorkflowState_SavesComplete;
  }
  async WorkflowState_SavesComplete(context = {}) {
    expireMyEffects.bind(this)(["1Action", "1Spell"]);
    if (configSettings.allowUseMacro && this.workflowOptions.noOnUseMacro !== true) {
      await this.callMacros(this.item, this.onUseMacros?.getMacros("postSave"), "OnUse", "postSave");
      if (this.ammunition) await this.callMacros(this.ammunition, this.ammunitionOnUseMacros?.getMacros("postSave"), "OnUse", "postSave");
    }
    const hasDamageRoll = this.activity.hasDamage || this.otherActivity?.hasDamage || this.activity.hasHealing || this.otherActivity?.hasHealing;
    if (!hasDamageRoll) return this.WorkflowState_AllRollsComplete;
    if (getAutoRollDamage(this) === "always") return this.WorkflowState_WaitForDamageRoll;
    const noHits = this.hitTargets.size === 0 && this.hitTargetsEC.size === 0;
    const allMissed = noHits && this.targets.size !== 0;
    if (allMissed) {
      if (configSettings.confirmAttackDamage !== "none" && !this.workflowOptions.forceCompletion)
        return this.WorkflowState_ConfirmRoll;
      if (this.workflowOptions.forceCompletion || configSettings.autoCompleteWorkflow) {
        expireMyEffects.bind(this)(["1Attack", "1Action", "1Spell"]);
        return this.WorkflowState_RollFinished;
      }
    }
    return this.WorkflowState_WaitForDamageRoll;
  }
  async WorkflowState_WaitForDamageRoll(context = {}) {
    if (context.damageRoll || context.otherDamageRoll) {
      return this.WorkflowState_ConfirmRoll;
    }
    const hasDamageRoll = this.activity.hasDamage || this.otherActivity?.hasDamage || this.activity.hasHealing || this.otherActivity?.hasHealing;
    if (!hasDamageRoll) return this.WorkflowState_AllRollsComplete;
    return super.WorkflowState_WaitForDamageRoll(context);
  }
  async WorkflowState_DamageRollComplete(context = {}) {
    await super.WorkflowState_DamageRollComplete(context);
    return this.WorkflowState_AllRollsComplete;
  }
};
var ActiveDefenceWorkflow = class extends Workflow {
  static {
    __name(this, "ActiveDefenceWorkflow");
  }
  async WorkflowState_WaitForAttackRoll(context) {
    this.workflowOptions.attackRollDSN = false;
    return super.WorkflowState_WaitForAttackRoll(context);
  }
  async processAttackRoll() {
    return super.processAttackRoll();
  }
  async WorkflowState_AttackRollComplete(context) {
    const attackBonus = (this.attackRoll?.total ?? 0) - (this.attackRoll?.dice[0]?.total ?? 0);
    this.activeDefenceDC = configSettings.optionalRules.activeDefenceModifier + attackBonus;
    return super.WorkflowState_AttackRollComplete(context);
  }
  async checkHits() {
    if (!this.attackRoll || !this.item) return;
    if (!this.attackRoll?.total) {
      this.abort = true;
      return this.performState(this.WorkflowState_Abort);
    }
    this.displayHitTargets(false);
    let hookId = Hooks.on("renderChatMessageHTML", this.processDefenceRoll.bind(this));
    try {
      this.hitTargets = /* @__PURE__ */ new Set();
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      this.defenceRequests = {};
      this.defenceTimeouts = {};
      this.activeDefenceRolls = {};
      this.isCritical = false;
      this.isFumble = false;
      await this.displayAttackRoll();
      await this.checkActiveAttacks(false, this.attackRoll);
      this.hitTargets = this.hitTargets;
      const isHitEC = false;
      for (let targetToken of this.targets) {
        let targetAC;
        let adRoll;
        const targetTokenDocument = getTokenDocument(targetToken);
        if (!targetTokenDocument) continue;
        if (this.activeDefenceRolls[targetTokenDocument.uuid ?? ""]) {
          adRoll = this.activeDefenceRolls[targetTokenDocument.uuid] ?? {};
          let attackType = "";
          const isHit = this.hitTargets.has(targetToken);
          let hitSymbol = "fa-blank";
          let isHitResult = "miss";
          if (game.user?.isGM && ["hitMiss", "hidden"].includes(configSettings.gmAttackDisplay ?? "full") && (this.isCritical || isHit || isHitEC)) {
            isHitResult = "miss";
            hitSymbol = "fa-xmark";
          } else if (this.isCritical) {
            isHitResult = "fumble";
            hitSymbol = "fa-xmarks-lines";
          } else if (game.user?.isGM && this.isFumble && ["hitMiss", "hidden"].includes(configSettings.gmAttackDisplay ?? "full")) {
            isHitResult = "hit";
            hitSymbol = "fa-check";
          } else if (this.isFumble) {
            isHitResult = "critical";
            hitSymbol = "fa-check-double";
          } else if (isHit) {
            isHitResult = "miss";
            hitSymbol = "fa-xmark";
          } else if (isHitEC && ["scale", "scaleNoAR"].includes(checkRule("challengeModeArmor"))) {
            isHitResult = "miss";
            hitSymbol = "fa-xmark";
          } else if (isHitEC) {
            isHitResult = "miss";
            hitSymbol = "fa-xmark";
          } else {
            isHitResult = "hit";
            hitSymbol = "fa-check";
          }
          let hitStyle = "";
          let img = targetTokenDocument.texture.src || targetTokenDocument.actor?.img;
          if (configSettings.usePlayerPortrait && targetTokenDocument.actor?.type === "character") {
            img = targetTokenDocument.actor?.img || targetTokenDocument.texture.src;
          }
          if (foundry.helpers.media.VideoHelper.hasVideoExtension(img ?? "")) {
            img = await game.video?.createThumbnail(img ?? "", { width: 100, height: 100 }) ?? "";
          }
          let acBonusString;
          const acDisplay = `${adRoll.total}${acBonusString ?? ""}`;
          const bonusAC = 0;
          let attackBonus = (this.activeDefenceDC ?? 0) - configSettings.optionalRules.activeDefenceModifier;
          const targetUuid = targetTokenDocument.uuid;
          this.hitDisplayData ??= {};
          this.hitDisplayData[targetUuid] = {
            targetClass: !!targetTokenDocument.actor?.hasPlayerOwner ? "midi-qol-player-target" : "midi-qol-npc-target",
            target: targetToken,
            actorUuid: targetTokenDocument.actor?.uuid,
            uuid: targetUuid,
            hitStyle,
            attackBonus,
            ac: (targetAC ?? 0) - attackBonus,
            acDisplay,
            baseAc: (targetAC ?? 0) - bonusAC,
            hitClass: ["hit", "critical", "isHitEC"].includes(isHitResult) ? "success" : "failure",
            acClass: targetTokenDocument.actor?.hasPlayerOwner ? "" : "midi-qol-npc-ac",
            hitSymbol,
            attackType,
            showAC: true,
            img: img ?? "",
            isHit,
            gmName: getTokenName(targetTokenDocument),
            playerName: getTokenPlayerName(targetTokenDocument) ?? "",
            hitResultNumeric: String(adRoll.total),
            attackTotal: adRoll.total
          };
        }
      }
    } catch (err) {
      TroubleShooter.recordError(err, "activeDefence");
    } finally {
      Hooks.off("renderChatMessageHTML", hookId);
    }
  }
  //@ts-expect-error no dnd5e types
  async checkActiveAttacks(whisper = false, roll, _coverBonus = 0) {
    const BasicRoll = CONFIG.Dice.BasicRoll;
    if (debugEnabled > 1) debug(`active defence : whisper ${whisper}  hit targets ${this.targets}`);
    if (this.targets.size <= 0) {
      return;
    }
    const D20Roll = CONFIG.Dice.D20Roll;
    const criticalTarget = 20 - (roll.options.criticalFailure ?? 1) + 1;
    const fumbleTarget = 20 - (roll.options.criticalSuccess ?? 20) + 1;
    let advantageMode = roll?.options?.advantageMode ?? D20Roll.NORMAL;
    let promises = [];
    for (let target of this.targets) {
      if (!target.actor) continue;
      const targetActor = target.actor;
      const targetActorSystem = target.actor.system;
      const midiFlagsAttackBonus = targetActor.flags?.[MODULE_ID]?.grants?.attack?.bonus;
      const actorAC = targetActorSystem.attributes.ac.value + (this.targetACModifiers.get(target.actor.uuid) ?? 0);
      let dcMod = actorAC - (22 - configSettings.optionalRules.activeDefenceModifier);
      let modString = `+ ${dcMod}`;
      if (dcMod < 0) modString = `- ${Math.abs(dcMod)}`;
      let attackBonus = 0;
      if (midiFlagsAttackBonus) {
        if (midiFlagsAttackBonus?.all) {
          const attackBonusRoll = await new Roll(midiFlagsAttackBonus.all, targetActor.getRollData())?.evaluate();
          attackBonus -= attackBonusRoll?.total ?? 0;
          foundry.utils.setProperty(this.actor, "flags.midi.evaluated.grants.attack.bonus.all", { value: attackBonus, effects: [`${targetActor.name}`] });
        }
        if (midiFlagsAttackBonus[this.activity.actionType]) {
          const attackBonusRoll = await new Roll(midiFlagsAttackBonus[this.activity.actionType], targetActor.getRollData()).evaluate();
          attackBonus -= attackBonusRoll?.total ?? 0;
          foundry.utils.setProperty(this.actor, `flags.midi.evaluated.grants.attack.bonus.${this.activity.actionType}`, { value: attackBonus, effects: [`${targetActor.name}`] });
        }
      }
      if (attackBonus < 0)
        modString = `${modString} - ${Math.abs(attackBonus)}`;
      else if (attackBonus > 0)
        modString = `${modString} + ${attackBonus}`;
      dcMod += attackBonus;
      let coverBonus = 0;
      const noCoverFlag = this.actor.flags?.[MODULE_ID]?.ignoreCover;
      let ignoreCover = false;
      if (noCoverFlag) {
        const conditionData = createConditionData({ workflow: this, target, actor: this.actor });
        ignoreCover ||= await evalAllConditionsAsync(this.actor, midiFlag2(`ignoreCover`), conditionData);
      }
      if (this.activity?.actionType === "rsak" && this.actor.flags?.dnd5e?.spellSniper) {
        const conditionData = createConditionData({ workflow: this, target, actor: this.actor });
        ignoreCover ||= await evalAllConditionsAsync(this.actor, `flags.dnd5e.spellSniper`, conditionData);
      }
      const sharpShooterFlag = this.actor.flags?.[MODULE_ID]?.sharpShooter || this.actor.flags?.dnd5e?.sharpShooter;
      if (this.activity?.actionType === "rwak" && sharpShooterFlag) {
        const conditionData = createConditionData({ workflow: this, target, actor: this.actor });
        ignoreCover ||= await evalAllConditionsAsync(this.actor, midiFlag2(`sharpShooter`), conditionData);
        ignoreCover ||= await evalAllConditionsAsync(this.actor, `flags.dnd5e.sharpShooter`, conditionData);
      }
      if (this.token) {
        const moduleCover = computeCoverBonus(this.token, target, this.activity);
        const statusCover = target.actor?.system?.attributes?.ac?.cover ?? 0;
        coverBonus = Math.max(moduleCover - statusCover, 0);
      }
      if (ignoreCover && coverBonus < TOTAL_COVER) coverBonus = 0;
      if (coverBonus) modString += ` + ${coverBonus}`;
      let formula = `1d20${modString}`;
      if (advantageMode === D20Roll.ADV_MODE.ADVANTAGE) {
        advantageMode = D20Roll.ADV_MODE.DISADVANTAGE;
      } else if (advantageMode === D20Roll.ADV_MODE.DISADVANTAGE) {
        advantageMode = D20Roll.ADV_MODE.ADVANTAGE;
      }
      const options = {
        criticalSuccess: criticalTarget,
        criticalFailure: fumbleTarget,
        advantageMode,
        target: this.activeDefenceDC
      };
      let player = playerFor(target);
      if (debugEnabled > 0) warn(`checkSaves | Player ${player?.name} controls actor ${target.actor.name} - requesting DC ${this.activeDefenceDC} save`);
      if (player && player.active && !player.isGM && target.actor) {
        promises.push(new Promise((resolve) => {
          const requestId = target.actor?.uuid ?? foundry.utils.randomID();
          this.defenceRequests[requestId] = resolve;
          if (player) requestPCActiveDefence(player, target.actor, this.item.name, requestId, { formula, rollOptions: options, bonus: dcMod, coverBonus, workflow: this });
          if (configSettings.playerSaveTimeout > 0) {
            this.defenceTimeouts[requestId] = setTimeout(async () => {
              if (this.defenceRequests[requestId]) {
                delete this.defenceRequests[requestId];
                delete this.defenceTimeouts[requestId];
                const result = await new D20Roll(formula, {}, options).evaluate();
                result.toMessage({ flavor: `${this.item.name} ${i18n("midi-qol.ActiveDefenceString")}` });
                resolve(result);
              }
            }, configSettings.playerSaveTimeout * 1e3);
          }
        }));
      } else {
        promises.push(
          new Promise(async (resolve) => {
            const result = await new D20Roll(formula, {}, options).evaluate();
            result.toMessage({ flavor: `${this.item.name} ${i18n("midi-qol.ActiveDefenceString")} ${options.criticalSuccess}/${options.criticalFailure}` }, { rollMode: configSettings.optionalRules.activeDefenceShow });
            resolve(result);
          })
        );
      }
    }
    if (debugEnabled > 1) debug("check saves: requests are ", this.saveRequests);
    var results = await Promise.all(promises);
    this.rollResults = results;
    let i = 0;
    for (let target of this.targets) {
      if (!target.actor) continue;
      if (!results[i]) {
        const message = `Token ${target?.name} ${getTokenDocument(target)?.uuid}, "could not roll active defence assuming 1`;
        error2(message, target);
        TroubleShooter.recordError(new Error(message), message);
        results[i] = await new BasicRoll("1").evaluate();
      }
      const result = results[i];
      let rollTotal = results[i]?.total || 0;
      this.isFumble = result.dice[0].total >= criticalTarget;
      this.isCritical = result.dice[0].total <= fumbleTarget;
      this.activeDefenceRolls ??= {};
      let isHit = this.isCritical || rollTotal < (this.activeDefenceDC ?? 0);
      if (isHit && this.activity.attack && this.attackRoll && target !== null && !foundry.utils.getProperty(this, `item.flags.${MODULE_ID}.noProvokeReaction`) && !this.workflowOptions.noProvokeReaction) {
        const workflowOptions = foundry.utils.mergeObject(this.workflowOptions, { sourceActorUuid: this.actor.uuid, sourceItemUuid: this.item.uuid }, { inplace: false, overwrite: true });
        let reaction;
        if (!this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
          const savedAC = target.actor.system.attributes.ac.value;
          reaction = await doReactions(target, this.tokenUuid, this.attackRoll, "reaction", { activity: this.activity, item: this.item, workflow: this, workflowOptions });
          const acChange = target.actor.system.attributes.ac.value - savedAC;
          rollTotal += (reaction.ac ?? 0) + acChange;
          results[i] = await new BasicRoll(`${rollTotal}`, {}, { type: "activeDefence" }).evaluate();
        }
      } else if (!isHit && this.activity.attack && this.attackRoll && target !== null && !foundry.utils.getProperty(this, `item.flags.${MODULE_ID}.noProvokeReaction`)) {
        const workflowOptions = foundry.utils.mergeObject(this.workflowOptions, { sourceActorUuid: this.actor.uuid, sourceItemUuid: this.item.uuid }, { inplace: false, overwrite: true });
        if (!this.item.flags?.[MODULE_ID]?.noProvokeReaction && !this.workflowOptions.noProvokeReaction) {
          const savedAC = target.actor.system.attributes.ac.value;
          let reaction = await doReactions(target, this.tokenUuid, this.attackRoll, "reactionmissed", { activity: this.activity, item: this.item, workflow: this, workflowOptions });
          const acChange = target.actor.system.attributes.ac.value - savedAC;
          rollTotal += (reaction.ac ?? 0) + acChange;
          results[i] = await new BasicRoll(`${rollTotal}`, {}, { type: "activeDefence" }).evaluate();
        }
      }
      isHit = this.isCritical || rollTotal < (this.activeDefenceDC ?? 0);
      this.activeDefenceRolls[getTokenDocument(target)?.uuid ?? ""] = results[i];
      if (isHit)
        this.hitTargets = this.hitTargets.add(target);
      else {
        this.hitTargets.delete(target);
        this.hitTargets = this.hitTargets;
      }
      if (game.user?.isGM) log(`Active defence: ${target.name} rolled ${rollTotal} vs attack DC ${this.activeDefenceDC}`, results[i]);
      i++;
    }
  }
  async displayAttackRoll() {
    const chatMessage = this.chatCard;
    let newFlags = {};
    const searchRe = /<div[^>]*?class="midi-qol-attack-roll[^"]*"[\s\S]*?<div class="end-midi-qol-attack-roll">/;
    let DCString = "DC";
    DCString = i18n("DND5E.AbbreviationDC") ?? "DC";
    const attackString = `<label class="midi-qol-active-defence-dc">${DCString} ${this.activeDefenceDC}</label> ${i18n("midi-qol.ActiveDefenceString")}`;
    const replaceString = `<div class="midi-qol-attack-roll midi-qol-section-filled"><div class="midi-roll-label">${attackString}</div><div class="end-midi-qol-attack-roll">`;
    let content = chatMessage?.content.replace(searchRe, replaceString) ?? "";
    newFlags = {
      "midi-qol": {
        isHit: this.hitTargets.size > 0,
        isHitEC: this.hitTargetsEC.size > 0,
        isCritical: this.isCritical,
        isFumble: this.isFumble,
        targets: Array.from(this.targets).map((t) => ({ uuid: getTokenDocument(t)?.uuid, name: getTokenDocument(t)?.name })),
        hitTargetUuids: Array.from(this.hitTargets).map((t) => getTokenDocument(t)?.uuid),
        hitECTargetUuids: Array.from(this.hitTargetsEC).map((t) => getTokenDocument(t)?.uuid)
      }
    };
    const rolls = [...this.attackRoll ? [this.attackRoll] : []];
    await this.performThrottledUpdate(chatMessage, { content, flags: newFlags, rolls });
  }
};
var UserWorkflow = class extends Workflow {
  static {
    __name(this, "UserWorkflow");
  }
  static get forceCreate() {
    return true;
  }
};
var DamageOnlyWorkflow = class extends Workflow {
  static {
    __name(this, "DamageOnlyWorkflow");
  }
  itemCard;
  constructor(actor, token, damageTotal, damageType, targets, roll, options) {
    if (options.storeWorkflow === void 0) options.storeWorkflow = true;
    if (!actor) actor = token.actor ?? targets[0]?.actor;
    const theTargets = Array.from(targets).map((t) => getToken(t)).filter((t) => !!t);
    let damageRoll = roll;
    if (!damageRoll) {
      damageRoll = new CONFIG.Dice.DamageRoll(`${damageTotal}`, {}, { type: damageType }).evaluateSync();
    }
    let theItem = null;
    const extraItemData = {
      name: options.flavor ?? "Damage Only Workflow",
      type: "feat",
      _id: foundry.utils.randomID(),
      system: {
        activities: {
          dnd5eactivity000: {
            type: "damage",
            _id: foundry.utils.randomID(),
            damage: {
              // @ts-expect-error no dnd5e-types
              parts: [{ custom: { enabled: true, formula: damageRoll.formula }, types: [damageRoll.options.type] }]
            },
            range: {
              override: true,
              units: ""
            },
            midiProperties: {
              ignoreTraits: [],
              triggeredActivityId: "none",
              triggeredActivityConditionText: "",
              triggeredActivityTargets: "targets",
              triggeredActivityRollAs: "self",
              forceConfigDialog: "default",
              forceRollDialog: "default",
              forceDamageDialog: "default",
              confirmTargets: "default",
              autoTargetType: "any",
              autoTargetAction: "default",
              automationOnly: false,
              otherActivityCompatible: true,
              identifier: ""
            },
            target: {
              affects: {
                type: "creature",
                special: ""
              }
            }
          }
        }
      }
    };
    if (options.item || options.itemData) {
      let itemData = options.item ? options.item.toObject() : options.itemData;
      if (itemData.system?.activities) delete itemData.system.activities;
      itemData = foundry.utils.mergeObject(itemData, extraItemData, { inplace: false });
      foundry.utils.setProperty(itemData, midiFlag2(`syntheticItem`), true);
      foundry.utils.setProperty(itemData, "_id", itemData._id ?? foundry.utils.randomID());
      theItem = new CONFIG.Item.documentClass(itemData, { parent: actor });
    } else {
      foundry.utils.setProperty(extraItemData, midiFlag2(`syntheticItem`), true);
      theItem = new CONFIG.Item.documentClass(extraItemData, { parent: actor });
    }
    if (!theItem) throw new Error("DamageOnlyWorkflow | could not create synthetic item");
    theItem.prepareData();
    theItem.prepareFinalAttributes();
    super(actor, theItem.system.activities.contents[0], ChatMessage.getSpeaker({ token, actor }), new Set(theTargets), { event: shiftOnlyEvent, workflowOptions: { noOnUseMacro: true } });
    this.flavor = options.flavor;
    this.defaultDamageType = GameSystemConfig.damageTypes[damageType]?.label ?? damageType;
    if (this.item) foundry.utils.setProperty(this.item, midiFlag2(`onUseMacroName`), null);
    this.stateTransitionCount = 0;
    if (options.itemCardId)
      foundry.utils.logCompatibilityWarning(
        "midi-qol | DamageOnlyWorkflow: itemCardId is deprecated, use itemCardUuid instead",
        { since: "midi-qol 12.4.32", until: "midi-qol 12.5.0" }
      );
    if (options.itemCardUuid) this.itemCardUuid = options.itemCardUuid;
    else {
      const message = game.messages?.get(options.itemCardId);
      if (message) this.itemCardUuid = message?.uuid;
    }
    this.setDamageRolls([damageRoll]).then(() => {
      this.damageTotal = damageTotal;
      this.isCritical = options.isCritical ?? false;
      this.kickStart = false;
      this.suspended = false;
      this.performState(this.WorkflowState_Start);
    });
    return this;
  }
  static get forceCreate() {
    return false;
  }
  async WorkflowState_Start(context = {}) {
    this.effectsAlreadyExpired = [];
    if (this.itemCardUuid === "new" || !fromUuidSync(this.itemCardUuid)) {
      const message = {};
      const messageConfig = foundry.utils.mergeObject({
        create: true,
        data: {
          flags: {
            dnd5e: {
              ...this.activity.messageFlags,
              messageType: "usage",
              use: {
                // @ts-expect-error TODO: is this right?
                effects: this.applicableEffects?.map((e) => e.id)
              }
            }
          },
          rolls: this.damageRolls
        },
        hasConsumption: false
      }, message);
      this.itemCard = await this.activity._createUsageMessage(messageConfig);
      this.itemCardUuid = this.itemCard?.uuid;
    }
    const whisperCard = configSettings.autoCheckHit === "whisper" || safeGetGameSetting("core", "rollMode") === CONST.DICE_ROLL_MODES.BLIND;
    if (this.actor) {
      const newRolls = await this.processDamageRollBonusFlags(this.damageRolls);
      await this.setDamageRolls(newRolls);
    }
    if (this.itemCardUuid) {
      this.isFumble = false;
      this.attackTotal = 9999;
      this.recordTargetACModifiers();
      await this.checkHits(this.workflowOptions);
      this.hitTargets = new Set(this.targets);
      await this.displayHits(whisperCard, false);
      await this.displayDamageRolls();
    } else {
      await this.damageRoll?.toMessage({ flavor: this.flavor });
    }
    this.hitTargets = new Set(this.targets);
    this.hitTargetsEC = /* @__PURE__ */ new Set();
    this.effectTargets = new Set(this.targets);
    const result = await this.WorkflowState_AllRollsComplete();
    return result;
  }
};
var TrapWorkflow = class extends Workflow {
  static {
    __name(this, "TrapWorkflow");
  }
  templateLocation;
  saveTargets;
  onUseMacroCalled;
  targetsPassedToConstructor;
  static get forceCreate() {
    return false;
  }
  constructor(actor, activity, targets, templateLocation = void 0, _trapSound = void 0, event = {}, _storeWorkflow = false) {
    super(actor, activity, ChatMessage.getSpeaker({ actor }), new Set(targets ?? []), { event, storeWorkflow: false });
    this.targetsPassedToConstructor = targets !== void 0;
    if (this.targetsPassedToConstructor && targets && targets.length > 0) {
      game.user?.targets.forEach((t) => t.setTarget(false, { releaseOthers: false }));
      game.user?.targets.clear();
      for (const t of targets) {
        t.setTarget(true, { releaseOthers: false });
        game.user?.targets.add(t);
      }
    }
    if (templateLocation) this.templateLocation = templateLocation;
    this.rollOptions.event = void 0;
    this.rollOptions.fastForwardAttack = true;
    this.rollOptions.fastForwardDamage = true;
    this.kickStart = false;
    this.suspended = false;
    this.activity.use({ configure: false, workflow: this, midiOptions: { workflowOptions: this.workflowOptions } }, {}, {});
    return this;
  }
  get workflowType() {
    return "TrapWorkflow";
  }
  async WorkflowState_Start(context = {}) {
    this.saveTargets = validTargetTokens(new Set(game.user?.targets));
    this.effectsAlreadyExpired = [];
    this.onUseMacroCalled = false;
    if (debugEnabled > 1) debug(" Trapworkflow | WorkflowState_Start ", this.item, getActivityAutoTargetAction2(this.activity), activityHasAreaTarget(this.activity), this.targets);
    return this.WorkflowState_AwaitTemplate;
  }
  async WorkflowState_AwaitTemplate(context = {}) {
    if (activityHasEmanationNoTemplate(this.activity)) {
      return this.WorkflowState_TemplatePlaced;
    }
    if (!activityHasAreaTarget(this.activity) || !this.templateLocation) {
      return this.WorkflowState_TemplatePlaced;
    }
    const TemplateClass = game.system.canvas.AbilityTemplate;
    const abilityTemplates = TemplateClass.fromActivity(this.activity);
    const templateData = abilityTemplates[0].document.toObject(false);
    templateData.x = this.templateLocation?.x || 0;
    templateData.y = this.templateLocation?.y || 0;
    templateData.direction = this.templateLocation?.direction || 0;
    let templates = await canvas.scene?.createEmbeddedDocuments("MeasuredTemplate", [templateData]);
    if (templates) {
      for (const tDoc of templates) {
        if (!tDoc?.uuid) continue;
        this.templateUuid = tDoc.uuid;
        await this.setupTemplateCleanup(tDoc);
      }
      this.placedTemplateCount = templates.length;
      this.selectAllTemplateTargets();
      if (this.templateLocation?.removeDelay) {
        let ids = templates.map((td) => td._id);
        setTimeout(() => canvas.scene?.deleteEmbeddedDocuments("MeasuredTemplate", ids), this.templateLocation.removeDelay * 1e3);
      }
    }
    return this.WorkflowState_TemplatePlaced;
  }
  async WorkflowState_TemplatePlaced(context = {}) {
    return this.WorkflowState_ValidateRoll;
  }
  async WorkflowState_ValidateRoll(context = {}) {
    return this.WorkflowState_WaitForAttackRoll;
  }
  async WorkflowState_WaitForAttackRoll(context = {}) {
    if (context.attackRoll) return this.WorkflowState_AttackRollComplete;
    if (!this.activity.attack && !this.activity.hasAttack) {
      this.hitTargets = new Set(this.targets);
      this.hitTargetsEC = /* @__PURE__ */ new Set();
      return this.WorkflowState_WaitForSaves;
    }
    warn("TrapWorkflow | WaitForAttackRoll rolling attack for", this.activity.name, "hasAttack:", this.activity.hasAttack, "attack:", this.activity.attack);
    this.activity.rollAttack?.({ workflow: this, midiOptions: { workflowOptions: this.workflowOptions } }, {}, {});
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_AttackRollComplete(context = {}) {
    await this.processAttackRoll();
    this.recordAttackStats();
    await this.displayAttackRoll();
    this.recordTargetACModifiers();
    await this.checkHits(this.workflowOptions);
    const whisperCard = configSettings.autoCheckHit === "whisper" || safeGetGameSetting("core", "rollMode") === CONST.DICE_ROLL_MODES.BLIND;
    await this.displayHits(whisperCard);
    return this.WorkflowState_WaitForSaves;
  }
  setDescriptorACModifiers() {
    throw new Error("Method not implemented.");
  }
  async WorkflowState_WaitForSaves(context = {}) {
    if (!this.activityHasSave) {
      this.saves = /* @__PURE__ */ new Set();
      const allHitTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
      this.failedSaves = new Set(allHitTargets);
      return this.WorkflowState_WaitForDamageRoll;
    }
    let hookId = Hooks.on("createChatMessage", this.processSaveRoll.bind(this));
    let monksId = Hooks.on("updateChatMessage", this.monksSavingCheck.bind(this));
    let flashId = Hooks.on("flash-rolls-5e.rollComplete", this.flashSavingCheck.bind(this));
    try {
      const saveDisplay2 = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
      await this.checkSaves(saveDisplay2 !== "allShow");
    } catch (err) {
      TroubleShooter.recordError(err, "checkSaves");
    } finally {
      Hooks.off("createChatMessage", hookId);
      Hooks.off("updateChatMessage", monksId);
      Hooks.off("flash-rolls-5e.rollComplete", flashId);
    }
    if (debugEnabled > 1) debug("Check Saves: renderChat message hooks length ", Hooks.events["renderChatMessageHTML"]?.length);
    const saveDisplay = (this.activity.saveDisplay ?? "default") === "default" ? configSettings.autoCheckSaves : this.activity.saveDisplay;
    await this.displaySaves(saveDisplay === "whisper");
    return this.WorkflowState_SavesComplete;
  }
  async WorkflowState_SavesComplete(context = {}) {
    return this.WorkflowState_WaitForDamageRoll;
  }
  async WorkflowState_WaitForDamageRoll(context = {}) {
    if (context.damageRoll) return this.WorkflowState_DamageRollComplete;
    if (!this.activity.hasDamage && !this.activity.hasHealing) return this.WorkflowState_AllRollsComplete;
    if (this.isFumble) {
      return this.WorkflowState_RollFinished;
    }
    if (debugEnabled > 1) debug("TrapWorkflow: Rolling damage ", this.rollOptions.event, this.spellLevel, this.workflowOptions.versatile, this.targets, this.hitTargets);
    this.rollOptions.fastForward = true;
    this.activity.rollDamage({ workflow: this, midiOptions: { ...this.rollOptions, workflowOptions: this.workflowOptions } }, {}, {});
    return this.WorkflowState_Suspend;
  }
  async WorkflowState_DamageRollComplete(context = {}) {
    await this.displayDamageRolls();
    if (this.isFumble) {
      return this.WorkflowState_ApplyDynamicEffects;
    }
    return this.WorkflowState_AllRollsComplete;
  }
  async WorkflowState_AllRollsComplete(context = {}) {
    if (debugEnabled > 1) debug("all rolls complete ", foundry.utils.duplicate(this.rawDamageDetail));
    if (this.rawDamageDetail?.length) await processDamageRoll(this, this.rawDamageDetail[0].type);
    this.recordDamageStats();
    return this.WorkflowState_ApplyDynamicEffects;
  }
  async WorkflowState_RollFinished(context = {}) {
    if (this.saveTargets && activityHasAreaTarget(this.activity)) {
      game.user?.targets.forEach((t) => {
        t.setTarget(false, { releaseOthers: false });
      });
      game.user?.targets.clear();
      this.saveTargets.forEach((t) => {
        t.setTarget(true, { releaseOthers: false });
        game.user?.targets.add(t);
      });
    }
    return super.WorkflowState_RollFinished;
  }
};
var DDBGameLogWorkflow = class extends Workflow {
  static {
    __name(this, "DDBGameLogWorkflow");
  }
  DDBGameLogHookId;
  needsDamage;
  needsOtherDamage;
  static get forceCreate() {
    return false;
  }
  static get(id) {
    return Workflow.getWorkflow(id);
  }
  constructor(actor, activity, speaker, targets, options) {
    super(actor, activity, speaker, targets, options);
    this.expectedTemplateCount = activityHasAreaTarget(this.activity) ? 1 : 0;
    this.templateUuids = [];
    this.needsDamage = !!this.activity.hasDamage;
    this.attackRolled = !activity.hasAttack;
    if (configSettings.undoWorkflow) this.undoData = { actorUuid: actor?.uuid, itemUuid: this.item.uuid, targets: Array.from(targets).map((t) => t.id), options };
    this.kickStart = true;
    this.flagTags = { "ddb-game-log": { "midi-generated": true } };
    if (configSettings.undoWorkflow) saveUndoData(this);
  }
  async complete() {
    if (this.chatCard) {
      await this.chatCard.update({
        flags: { "midi-qol": { type: MESSAGE_TYPES.HITS } }
      });
    }
  }
  async WorkflowState_PreambleComplete(context = {}) {
    return super.WorkflowState_PreambleComplete(context);
  }
  async WorkflowState_WaitForAttackRoll(context = {}) {
    if (context.attackRoll) return this.WorkflowState_AttackRollComplete;
    if (!this.activity.attack) {
      return this.WorkflowState_AttackRollComplete;
    }
    if (!this.attackRolled) return this.WorkflowState_Suspend;
    return this.WorkflowState_AttackRollComplete;
  }
  async WorkflowState_AttackRollComplete(context = {}) {
    this.effectsAlreadyExpired = [];
    if (checkRule("removeHiddenInvis")) await removeHidden.bind(this)();
    await this.callMidiHooks("midi-qol.preCheckHits");
    if (debugEnabled > 1) debug(this.attackRollHTML);
    if (configSettings.autoCheckHit !== "none" && this.activity.attack) {
      this.recordTargetACModifiers();
      await this.checkHits(this.workflowOptions);
      await this.displayHits(configSettings.autoCheckHit === "whisper");
    }
    await this.callMidiHooks("midi-qol.AttackRollComplete");
    if (this.aborted) return this.WorkflowState_Abort;
    return this.WorkflowState_WaitForDamageRoll;
  }
  async WorkflowState_AwaitTemplate(context = {}) {
    if (!activityHasAreaTarget(this.activity)) return super.WorkflowState_AwaitTemplate;
    let system = game.system;
    const template = system.canvas.AbilityTemplate.Activity(this.activity);
    if (template) template.drawPreview();
    return super.WorkflowState_AwaitTemplate;
  }
  async WorkflowState_WaitForDamageRoll(context = {}) {
    if (this.needsDamage && this.activity.hasDamage) return this.WorkflowState_Suspend;
    if (this.needsOtherDamage) return this.WorkflowState_Suspend;
    if (context.attackRoll) return this.WorkflowState_AttackRollComplete;
    const allHitTargets = /* @__PURE__ */ new Set([...this.hitTargets, ...this.hitTargetsEC]);
    this.failedSaves = new Set(allHitTargets);
    if (!this.activity.hasDamage && !this.activity.hasHealing) return this.WorkflowState_WaitForSaves;
    return this.WorkflowState_DamageRollComplete;
  }
  async WorkflowState_DamageRollComplete(context = {}) {
    this.defaultDamageType = getActivityDefaultDamageType(this) ?? this.defaultDamageType ?? MidiQOL.MQdefaultDamageType;
    if (this.activity.actionType === "heal" && !Object.keys(GameSystemConfig.healingTypes).includes(this.defaultDamageType ?? "")) this.defaultDamageType = "healing";
    this.rawDamageDetail = createDamageDetail({ roll: this.damageRolls, activity: this.activity, defaultType: this.defaultDamageType });
    const damageBonusMacros = this.getDamageBonusMacros();
    if (damageBonusMacros) {
      await this.rollBonusDamage(damageBonusMacros);
    }
    this.rawOtherDamageDetail = [];
    if (this.bonusDamageRolls) {
      const messageData = {
        flavor: this.bonusDamageFlavor,
        speaker: this.speaker
      };
      this.bonusDamageRoll?.toMessage(messageData);
    }
    expireMyEffects.bind(this)(["1Attack", "1Action", "1Spell"]);
    if (getActivityAutoTargetAction2(this.activity) === "none" && activityHasAreaTarget(this.activity) && !this.activity.attack) {
      this.targets = validTargetTokens(new Set(game.user?.targets));
      this.hitTargets = validTargetTokens(new Set(game.user?.targets));
      this.hitTargetsEC = /* @__PURE__ */ new Set();
    }
    if (this.isFumble) {
      return this.WorkflowState_RollFinished;
    }
    if (this.activity.save) return this.WorkflowState_WaitForSaves;
    return this.WorkflowState_AllRollsComplete;
  }
  async WorkflowState_RollFinished(context = {}) {
    if (this.postSummonHookId) Hooks.off("dnd5e.postSummon", this.postSummonHookId);
    await super.WorkflowState_RollFinished();
    return this.WorkflowState_Suspend;
  }
};
var DummyWorkflow = class extends Workflow {
  static {
    __name(this, "DummyWorkflow");
  }
  static get forceCreate() {
    return false;
  }
  expectedAttackRoll;
  _dummyActor;
  //@ts-expect-error dnd5e v10
  constructor(actor, activity, speaker, targets, options) {
    options.noTemplateHook = true;
    super(actor, activity, speaker, targets, options);
    this._dummyActor = actor;
    this.advantage = options?.advantage;
    this.disadvantage = options?.disadvantage;
    this.rollOptions.fastForward = options?.fastForward;
  }
  get actor() {
    return this._dummyActor ?? super.actor;
  }
  async performState(newState) {
    return super.performState(this.WorkflowState_Suspend);
  }
  async simulateSave(targets) {
    this.targets = new Set(targets);
    this.hitTargets = new Set(targets);
    this.initSaveResults();
    await this.checkSaves(true, true);
    for (let result of this.saveResults) {
      result.saveAdvantage = result.options.advantageMode === 1;
      result.saveDisadvantage = result.options.advantageMode === -1;
      const D20Roll = CONFIG.Dice.D20Roll;
      result.saveRoll = await new D20Roll(result.formula).evaluate();
      const maxroll = (await result.saveRoll?.reroll({ maximize: true }))?.total;
      const minroll = (await result.saveRoll?.reroll({ minimize: true }))?.total;
      result.expectedSaveRoll = ((maxroll || 0) + (minroll || 0)) / 2;
      if (result.saveAdvantage) result.expectedSaveRoll += 3.325;
      if (result.saveDisadvantage) result.expectedSaveRoll -= 3.325;
    }
    return this;
  }
  async simulateAttack(target) {
    this.targets = /* @__PURE__ */ new Set([target]);
    await this.checkAttackAdvantage();
    const hookId = Hooks.on("dnd5e.rollAttack", (item, roll, ammoUpdate) => {
      if (item === this.item && ammoUpdate?.length) ammoUpdate.length = 0;
    });
    try {
      [this.attackRoll] = await this.activity.rollAttack?.({ midiOptions: { fastForward: true, chatMessage: false, isDummy: true, workflowOptions: this.workflowOptions } }, {}, {});
    } catch (err) {
      TroubleShooter.recordError(err, "simulate attack");
    } finally {
      Hooks.off("dnd5e.rollAttack", hookId);
    }
    const maxroll = (await this.attackRoll?.reroll({ maximize: true }))?.total;
    const minroll = (await this.attackRoll?.reroll({ minimize: true }))?.total;
    this.expectedAttackRoll = ((maxroll || 0) + (minroll || 0)) / 2;
    if (this.advantage) this.expectedAttackRoll += 3.325;
    if (this.disadvantage) this.expectedAttackRoll -= 3.325;
    return this;
  }
};
var WorkflowRegistry = {
  Workflow,
  SavesFirstWorkflow,
  ActiveDefenceWorkflow,
  UserWorkflow,
  DamageOnlyWorkflow,
  TrapWorkflow,
  DDBGameLogWorkflow,
  DummyWorkflow
};

// src/module/settings.ts
var LaunchWizardSubmenu = class extends FormApplication {
  static {
    __name(this, "LaunchWizardSubmenu");
  }
  constructor(object = {}, options = {}) {
    super(object, options);
  }
  static get defaultOptions() {
    return foundry.utils.mergeObject(super.defaultOptions, {
      id: "midi-qol-launch-wizard",
      classes: ["midi-qol"],
      title: "Launch Setup Wizard",
      width: 0,
      height: 0
    });
  }
  async _render(force, options) {
    Hooks.call("midi-qol-setup-wizard.launch");
    this.close();
  }
  getData() {
    return {};
  }
  _updateObject(event, formData) {
    return Promise.resolve();
  }
};
var ResetSettingsSubmenu = class extends FormApplication {
  static {
    __name(this, "ResetSettingsSubmenu");
  }
  constructor(object = {}, options = {}) {
    super(object, options);
  }
  static get defaultOptions() {
    return foundry.utils.mergeObject(super.defaultOptions, {
      id: "midi-qol-reset-settings",
      classes: ["midi-qol"],
      title: "Reset Settings",
      width: 0,
      height: 0
    });
  }
  async _render(force, options) {
    const confirmed = await foundry.applications.api.DialogV2.confirm({
      window: { title: i18n("midi-qol.ResetDefaults.Title") },
      content: `<p>${i18n("midi-qol.ResetDefaults.Content")}</p>`,
      yes: { label: i18n("midi-qol.ResetDefaults.Confirm") },
      no: { label: i18n("Cancel") }
    });
    if (confirmed) {
      await resetConfigSettingsToDefaults2();
    }
    this.close();
  }
  getData() {
    return {};
  }
  _updateObject(event, formData) {
    return Promise.resolve();
  }
};
var fastForwardMigrationDone = false;
var autoRemoveTargets;
var coloredBorders;
var criticalDamage;
var criticalDamageGM;
var debounceInterval;
var dragDropTargeting;
var enableWorkflow;
var forceHideRoll;
var itemRollButtons;
var midiSoundSettings = {};
var midiSoundSettingsBackup = void 0;
var nsaFlag;
var playerControlsInvisibleTokens = false;
var pruneChatLog = true;
var replaceDefaultActivities = true;
var isDnd5e53 = false;
function setDnd5e53(value) {
  isDnd5e53 = value;
}
__name(setDnd5e53, "setDnd5e53");
var saveToChatCard = false;
var targetConfirmation;
var useWeakReferences = false;
function syncChatlogPruneSetting(enabled) {
  const chatlogPruneModule = game.modules.get("chatlog-prune");
  if (!chatlogPruneModule?.active) return;
  try {
    const currentSetting = game.settings.get("chatlog-prune", "enabled");
    if (currentSetting !== enabled) {
      game.settings.set("chatlog-prune", "enabled", enabled);
    }
  } catch (err) {
    console.warn("midi-qol | Failed to sync chatlog-prune enabled setting:", err);
  }
}
__name(syncChatlogPruneSetting, "syncChatlogPruneSetting");
var defaultTargetConfirmationSettings = {
  enabled: false,
  always: false,
  noneTargeted: false,
  hasAttack: false,
  hasCreatureTarget: false,
  targetSelf: false,
  hasAoE: false,
  hasRangedAoE: false,
  longRange: false,
  inCover: false,
  allies: false,
  mixedDisposition: false,
  gridPosition: { x: 0, y: 0 }
};
var defaultKeyMapping = {
  "DND5E.Advantage": "altKey",
  "DND5E.Disadvantage": "ctrlKey",
  "DND5E.Critical": "altKey",
  "DND5E.Versatile": "shiftKey"
};
var ConfigSettings = class {
  static {
    __name(this, "ConfigSettings");
  }
  // fullAuto: boolean = false;
  addChatDamageButtons = "none";
  addDead = "none";
  addFakeDice = false;
  addWounded = 0;
  addWoundedStyle = "none";
  activityNamePrefix = true;
  conditionEditorEnabled = true;
  activationAutomation = "chat";
  midiWoundedCondition = "none";
  midiDeadCondition = "none";
  midiUnconsciousCondition = "none";
  allowUseMacro = true;
  allowActorUseMacro = true;
  attackPerTarget = false;
  autoApplyDamage = "none";
  damageCardStyle = "flexbox";
  playerDamageCard = "none";
  playerCardDamageDifferent = false;
  hidePlayerDamageCard = true;
  autoCEEffects = "none";
  autoCheckHit = "none";
  autoCheckSaves = "none";
  autoFastForward = [];
  autoItemEffects = "off";
  autoRemoveSummonedCreature = false;
  autoUndoTransformations = false;
  autoRemoveTemplate = true;
  autoRemoveInstantaneousTemplate = false;
  constrainTemplatePlacement = false;
  templateSnapping = "full";
  autoRollAttack = false;
  autoRollDamage = "none";
  autoCompleteWorkflow = true;
  saveDROrder = "DRSaveDr";
  autoTarget = "none";
  averageDamage = "none";
  checkSaveText = false;
  checkTwoHanded = false;
  chooseAmmunition = true;
  collapsibleTargets = false;
  coloredBorders = "none";
  concentrationAutomation = false;
  confirmAmmunition = false;
  gmConfirmAmmunition = false;
  consumeResource = "none";
  convenientEffectsReaction = "Reaction";
  criticalDamage = "default";
  criticalDamageGM = "none";
  criticalSound = "";
  customSoundsPlaylist = "none";
  damageImmunities = "none";
  damageImmunityMultiplier = 0;
  damageResistanceMultiplier = 0.5;
  damageVulnerabilityMultiplier = 2;
  defaultSaveMultiplier = 0.5;
  diceSound = "";
  displaySaveAdvantage = true;
  doConcentrationCheck = "chat";
  griddedGridless = false;
  gridlessFudge = 0;
  doReactions = "all";
  enableDDBGL = false;
  enforceReactions = "none";
  enforceBonusActions = "none";
  enforceSingleWeaponTarget = false;
  fixStickyKeys = true;
  fumbleSound = "";
  ghostRolls = false;
  gmAttackDisplay = "full";
  gmDamageDisplay = "full";
  gmAttackDsn = "show";
  gmDamageDsn = "show";
  gmAttackDamageCards = false;
  gmAutoAttack = false;
  gmAutoDamage = "none";
  gmAutoFastForward = [];
  gmConsumeResource = "none";
  gmDoReactions = "all";
  gmHide3dDice = false;
  gmRemoveButtons = "all";
  preferredGM = "";
  hideRollDetails = "none";
  ignoreSpellReactionRestriction = false;
  itemTypeList = void 0;
  itemUseSound = "";
  keepRollStats = false;
  keyMapping = defaultKeyMapping;
  mergeCardCondensed = false;
  addSheetButtons = true;
  mergeCardMulti = false;
  mergeCardMultiDamage = false;
  displayProperties = true;
  midiPropertiesTabRole = CONST.USER_ROLES.PLAYER;
  confirmAttackDamage = "none";
  highlightSuccess = true;
  highLightCriticalAttackOnly = false;
  markPlayerDefeated = false;
  markNonPlayerDefeated = false;
  optionalRulesEnabled = false;
  paranoidGM = false;
  playerRollSaves = "none";
  playerSaveTimeout = 0;
  playerStatsOnly = false;
  potionUseSound = "";
  quickSettings = true;
  rangeTarget = "none";
  useTemplateRangedTargeting = false;
  reactionTimeout = 10;
  recordAOO = "none";
  removeButtons = "all";
  removeConcentration = true;
  removeConcentrationEffects = "effects";
  playerRequireAmmunition = false;
  gmRequireAmmunition = false;
  requireMagical = "off";
  requiresTargets = "none";
  rollNPCLinkedSaves = "auto";
  rollNPCSaves = "auto";
  rollChecksBlind = [];
  rollSavesBlind = [];
  rollSkillsBlind = [];
  savesBeforeDamage = false;
  saveStatsEvery = 20;
  showDSN = true;
  showFastForward = false;
  showItemDetails = "all";
  showReactionAttackRoll = "all";
  showReactionChatMessage = false;
  singleConcentrationRoll = true;
  skillAbilityCheckAdvantage = true;
  spellHitSound = "";
  spellUseSound = "";
  spellUseSoundRanged = "";
  tempHPDamageConcentrationCheck = false;
  concentrationIncapacitatedConditionCheck = false;
  toggleOptionalRules = false;
  useCustomSounds = true;
  usePlayerPortrait = false;
  useTokenNames = false;
  undoChatColor = "Delete";
  undoWorkflow = false;
  useDamageDetail = true;
  waitForDamageApplication = true;
  weaponHitSound = "";
  weaponUseSound = "";
  weaponUseSoundRanged = "";
  rollAlternate = "off";
  optionalRules = {
    actionSpecialDurationImmediate: false,
    activeDefence: false,
    activeDefenceModifier: 12,
    activeDefenceShow: "selfroll",
    ActiveDefenceDC: true,
    autoRerollInitiative: false,
    challengeModeArmor: "none",
    checkFlanking: "off",
    flankingBonus: "advantage",
    flankingRequiresVisibility: false,
    showFlankingHighlight: false,
    checkRange: "longFail",
    coverCalculation: "none",
    criticalNat20: false,
    criticalSaves: false,
    displayBonusRolls: true,
    distanceIncludesHeight: false,
    distanceMeasurement: "center",
    // "center" = grid square centers, "perimeter" = token perimeters
    DRAllPerDamageDetail: true,
    hiddenAdvantage: "none",
    incapacitated: "nothing",
    invisAdvantage: "RAW2024",
    invisVision: false,
    maxDRValue: false,
    nearbyAllyRanged: 0,
    nearbyFoe: 5,
    optionalCritRule: -1,
    removeHiddenInvis: true,
    vitalityItem: "",
    // Item name for vitality pool feature
    vitalityDeadCondition: "dead",
    // Condition to apply when vitality reaches 0
    vitalityDeathRequires: "vitalityOnly",
    // "vitalityOnly" = dead when vitality=0, "vitalityAndHP" = dead when vitality=0 AND HP=0
    wallsBlockRange: "center"
  };
};
var configSettings = new ConfigSettings();
function checkMechanic(mechanic) {
  if (configSettings.toggleOptionalRules) return false;
  return configSettings.optionalRules[mechanic];
}
__name(checkMechanic, "checkMechanic");
function checkRule(rule) {
  let rulesEnabled = configSettings.optionalRulesEnabled;
  if (game.user?.isGM)
    rulesEnabled = rulesEnabled ? !configSettings.toggleOptionalRules : configSettings.toggleOptionalRules;
  return rulesEnabled && configSettings.optionalRules[rule];
}
__name(checkRule, "checkRule");
async function resetConfigSettingsToDefaults2() {
  const defaultSettings = new ConfigSettings();
  await game.settings.set("midi-qol", "ConfigSettings", defaultSettings);
  configSettings = defaultSettings;
  await game.settings.set("midi-qol", "EnableWorkflow", true);
  await game.settings.set("midi-qol", "ReplaceDefaultActivities", true);
  await game.settings.set("midi-qol", "showGM", false);
  await game.settings.set("midi-qol", "ForceHideRoll", true);
  await game.settings.set("midi-qol", "DragDropTarget", false);
  await game.settings.set("midi-qol", "AutoRemoveTargets", "dead");
  await game.settings.set("midi-qol", "TargetConfirmation", defaultTargetConfirmationSettings);
  await game.settings.set("midi-qol", "playerControlsInvisibleTokens", false);
  await game.settings.set("midi-qol", "pruneChatLog", true);
  await game.settings.set("midi-qol", "DebounceInterval", 0);
  await game.settings.set("midi-qol", "Debug", "None");
  await game.settings.set("midi-qol", "SaveToChatCard", false);
  await game.settings.set("midi-qol", "UseWeakReferences", false);
  await game.settings.set("midi-qol", "splashWarnings", true);
  Hooks.callAll("midi-qol.ConfigSettingsChanged");
  ui.notifications?.info("midi-qol | Settings reset to defaults");
}
__name(resetConfigSettingsToDefaults2, "resetConfigSettingsToDefaults");
var checkedModuleList = [
  "about-time",
  "ActiveAuras",
  "auraeffects",
  "autoanimations",
  "anonymous",
  "babonus",
  "chris-premades",
  "condition-lab-triggler",
  "dae",
  "ddb-importer",
  "ddb-game-log",
  "dice-so-nice",
  "effectmacro",
  "gambits-premades",
  "itemacro",
  /levels/,
  "lib-wrapper",
  "midi-item-showcase-community",
  "multilevel-tokens",
  "ready-set-roll-5e",
  "sequencer",
  "simbuls-cover-calculator",
  "socketlib",
  "times-up",
  "tokenmagic",
  "vision-5e",
  "walledtemplates",
  "warpgate",
  "wjmaia"
];
var CheckedAuthorsList = [
  "hell",
  "dfreds",
  "theripper93",
  "ironmonk"
];
function collectSettingData() {
  const cleanConfigSettings = { ...configSettings };
  delete cleanConfigSettings.displaySaveDC;
  let data = {
    configSettings: cleanConfigSettings,
    midiSoundSettings,
    itemRollButtons,
    autoRemoveTargets,
    coloredBorders,
    criticalDamage,
    criticalDamageGM,
    debounceInterval,
    dragDropTargeting,
    enableWorkflow,
    forceHideRoll,
    nsaFlag,
    saveToChatCard,
    playerControlsInvisibleTokens,
    pruneChatLog,
    replaceDefaultActivities,
    targetConfirmation,
    useWeakReferences,
    flags: {}
  };
  data.flags["exportSource"] = {
    system: game.system?.id,
    coreVersion: game.version ?? game.version,
    systemVersion: game.system.version,
    midiVersion: game.modules.get("midi-qol")?.version
  };
  return data;
}
__name(collectSettingData, "collectSettingData");
function collectModuleData() {
  const modules = {};
  for (const moduleData of game.modules ?? []) {
    const mod = moduleData;
    if (!mod.active && !checkedModuleList.includes(mod.id)) continue;
    let idToUse = mod.id;
    let titleToUse = mod.title;
    if (idToUse.match(/plutonium/i) || titleToUse.match(/plutonium/i)) {
      idToUse = idToUse.replace(/plutonium/i, "xxxxxxxxx");
      titleToUse = titleToUse.replace(/plutonium/i, "xxxxxxxxx");
    }
    modules[idToUse] = {
      title: titleToUse,
      active: mod.active,
      installed: true,
      version: mod.version,
      compatibility: mod.compatibility?.verified
    };
  }
  return modules;
}
__name(collectModuleData, "collectModuleData");
function exportSettingsToJSON() {
  const data = collectSettingData();
  data.modules = collectModuleData();
  const filename = `fvtt-midi-qol-settings.json`;
  foundry.utils.saveDataToFile(JSON.stringify(data, null, 2), "text/json", filename);
}
__name(exportSettingsToJSON, "exportSettingsToJSON");
function removeOldValues(theObject) {
  if (theObject == null || typeof theObject !== "object") return theObject;
  Object.keys(theObject).forEach((key) => {
    if (theObject[key]?.newValue !== void 0) {
      theObject[key] = theObject[key].newValue;
    }
  });
  return theObject;
}
__name(removeOldValues, "removeOldValues");
function migrateGmRollHiding(cs) {
  if (cs.gmAttackDisplay !== void 0) return;
  const old = cs.hideRollDetails ?? "none";
  const ghost = cs.ghostRolls ?? false;
  const migrationMap = {
    "none": { attack: "full", damage: "full", attackDsn: "show", damageDsn: "show" },
    "detailsDSN": { attack: "hideFormula", damage: "full", attackDsn: "show", damageDsn: "show" },
    "details": { attack: "hideFormula", damage: "full", attackDsn: "none", damageDsn: "none" },
    "d20Only": { attack: "d20Only", damage: "totalOnly", attackDsn: "show", damageDsn: "none" },
    "d20DamageDSN": { attack: "d20Only", damage: "totalOnly", attackDsn: "show", damageDsn: "show" },
    "hitDamage": { attack: "hitMiss", damage: "totalOnly", attackDsn: "none", damageDsn: "none" },
    "hitCriticalDamage": { attack: "hitMissCrit", damage: "totalOnly", attackDsn: "none", damageDsn: "none" },
    "attackTotalOnly": { attack: "attackTotal", damage: "totalOnly", attackDsn: "none", damageDsn: "none" },
    "d20AttackOnly": { attack: "d20Only", damage: "hidden", attackDsn: "show", damageDsn: "none" },
    "all": { attack: "hidden", damage: "hidden", attackDsn: "none", damageDsn: "none" }
  };
  const mapped = migrationMap[old] ?? migrationMap["none"];
  cs.gmAttackDisplay = mapped.attack;
  cs.gmDamageDisplay = mapped.damage;
  cs.gmAttackDsn = ghost ? "ghost" : mapped.attackDsn;
  cs.gmDamageDsn = ghost ? "ghost" : mapped.damageDsn;
  delete cs.hideRollDetails;
  delete cs.ghostRolls;
  delete cs.gmHide3dDice;
}
__name(migrateGmRollHiding, "migrateGmRollHiding");
async function migrateDisplaySaveDC(configSettingsToMigrate, source = "startup") {
  const oldMidiDisplaySaveDC = configSettingsToMigrate.displaySaveDC;
  const currentDnd5eVisibility = safeGetGameSetting("dnd5e", "challengeVisibility");
  let newVisibility = currentDnd5eVisibility;
  if (oldMidiDisplaySaveDC === false || currentDnd5eVisibility === "none") {
    newVisibility = "none";
  }
  const needsChange = newVisibility !== currentDnd5eVisibility;
  if (needsChange) {
    await game.settings.set("dnd5e", "challengeVisibility", newVisibility);
  }
  delete configSettingsToMigrate.displaySaveDC;
  if (game.user === game.users?.activeGM) {
    const sourceText = source === "import" ? "The imported settings contained" : "Your midi-qol settings contained";
    let messageContent = `<h3>Midi-QOL Settings Migration</h3>`;
    messageContent += `<p>${sourceText} the deprecated <strong>Display Saving Throw DC</strong> setting. It has been migrated to use the dnd5e <strong>Challenge Visibility</strong> setting.</p>`;
    messageContent += `<ul>`;
    messageContent += `<li>Old displaySaveDC: <strong>${oldMidiDisplaySaveDC ? "Show DC" : "Hide DC"}</strong></li>`;
    messageContent += `<li>Previous dnd5e challengeVisibility: <strong>${currentDnd5eVisibility}</strong></li>`;
    if (needsChange) {
      messageContent += `<li>Updated dnd5e challengeVisibility to: <strong>${newVisibility}</strong> (most restrictive)</li>`;
    } else {
      messageContent += `<li>dnd5e challengeVisibility unchanged: <strong>${newVisibility}</strong></li>`;
    }
    messageContent += `</ul>`;
    messageContent += `<p><em>You can control this setting from the dnd5e settings or from the midi-qol config panel under Workflow \u2192 Saves.</em></p>`;
    ChatMessage.create({
      content: messageContent,
      whisper: ChatMessage.getWhisperRecipients("GM"),
      speaker: { alias: "Midi-QOL" }
    });
  }
}
__name(migrateDisplaySaveDC, "migrateDisplaySaveDC");
async function importSettingsFromJSON(json) {
  let requiresReload = false;
  if (typeof json === "string")
    json = JSON.parse(json);
  let savedModules = json.modules;
  if (json.midiSettings) {
    savedModules = savedModules ?? json.midiSettings.modules;
    json = json.midiSettings;
  }
  savedModules = savedModules ?? json.modules;
  const importedConfigSettings = removeOldValues(json.configSettings);
  if (importedConfigSettings.displaySaveDC !== void 0) {
    await migrateDisplaySaveDC(importedConfigSettings, "import");
  }
  migrateGmRollHiding(importedConfigSettings);
  await game.settings.set("midi-qol", "ConfigSettings", importedConfigSettings);
  await game.settings.set("midi-qol", "MidiSoundSettings", removeOldValues(json.midiSoundSettings) ?? {});
  await game.settings.set("midi-qol", "TargetConfirmation", removeOldValues(json.targetConfirmation));
  if (json.autoFastForwardAbilityRolls) {
    const cs = importedConfigSettings;
    if (!Array.isArray(cs.autoFastForward)) cs.autoFastForward = [];
    if (!Array.isArray(cs.gmAutoFastForward)) cs.gmAutoFastForward = [];
    for (const rollType of ["check", "save", "skill", "tool"]) {
      if (!cs.autoFastForward.includes(rollType)) cs.autoFastForward.push(rollType);
      if (!cs.gmAutoFastForward.includes(rollType)) cs.gmAutoFastForward.push(rollType);
    }
    await game.settings.set("midi-qol", "ConfigSettings", cs);
  }
  await game.settings.set("midi-qol", "AutoRemoveTargets", removeOldValues(json.autoRemoveTargets));
  if (json.coloredBorders !== void 0 && !importedConfigSettings.coloredBorders) {
    let cb = removeOldValues(json.coloredBorders);
    if (cb === "None") cb = "none";
    importedConfigSettings.coloredBorders = cb;
    await game.settings.set("midi-qol", "ConfigSettings", importedConfigSettings);
  }
  if (json.criticalDamage !== void 0 && !importedConfigSettings.criticalDamage) {
    importedConfigSettings.criticalDamage = removeOldValues(json.criticalDamage);
    if (importedConfigSettings.criticalDamage === "none") importedConfigSettings.criticalDamage = "default";
  }
  if (json.criticalDamageGM !== void 0 && !importedConfigSettings.criticalDamageGM) {
    importedConfigSettings.criticalDamageGM = removeOldValues(json.criticalDamageGM);
    if (importedConfigSettings.criticalDamageGM === "none") importedConfigSettings.criticalDamageGM = importedConfigSettings.criticalDamage ?? "default";
  }
  if (json.criticalDamage !== void 0 || json.criticalDamageGM !== void 0) {
    await game.settings.set("midi-qol", "ConfigSettings", importedConfigSettings);
  }
  await game.settings.set("midi-qol", "DebounceInterval", removeOldValues(json.debounceInterval));
  await game.settings.set("midi-qol", "DragDropTarget", removeOldValues(json.dragDropTargeting));
  await game.settings.set("midi-qol", "EnableWorkflow", removeOldValues(json.enableWorkflow));
  if (json.forceHideRoll !== void 0) {
    if (game.settings.get("midi-qol", "ForceHideRoll") !== json.forceHideRoll) requiresReload = true;
    await game.settings.set("midi-qol", "ForceHideRoll", removeOldValues(json.forceHideRoll));
  }
  if (json.replaceDefaultActivities !== void 0) {
    if (game.settings.get("midi-qol", "ReplaceDefaultActivities") !== json.replaceDefaultActivities) requiresReload = true;
    await game.settings.set("midi-qol", "ReplaceDefaultActivities", removeOldValues(json.replaceDefaultActivities));
  }
  await game.settings.set("midi-qol", "SaveToChatCard", removeOldValues(json.saveToChatCard));
  await game.settings.set("midi-qol", "showGM", removeOldValues(json.nsaFlag));
  await game.settings.set("midi-qol", "UseWeakReferences", removeOldValues(json.useWeakReferences));
  if (json.playerControlsInvisibleTokens !== void 0) {
    if (game.settings.get("midi-qol", "playerControlsInvisibleTokens") !== json.playerControlsInvisibleTokens) requiresReload = true;
    await game.settings.set("midi-qol", "playerControlsInvisibleTokens", json.playerControlsInvisibleTokens);
  }
  if (json.pruneChatLog !== void 0) {
    if (game.settings.get("midi-qol", "pruneChatLog") !== json.pruneChatLog) requiresReload = true;
    await game.settings.set("midi-qol", "pruneChatLog", json.pruneChatLog);
  }
  const settingsAppId = game.settings.sheet?.id;
  if (settingsAppId) foundry.applications.instances.get(settingsAppId)?.render(true);
  const exportSource = json.flags?.exportSource;
  ui.notifications?.notify(`Importing settings from foundry version ${exportSource?.coreVersion} dnd ${exportSource?.systemVersion} midi ${exportSource?.midiVersion}`);
  if (savedModules && Object.keys(savedModules).length > 0) {
    const activeModules = Object.fromEntries(
      Object.entries(savedModules).filter(([_, mod]) => mod.active)
    );
    const hasMismatch = Object.entries(activeModules).some(([id, mod]) => {
      const current = game.modules.get(id);
      if (!current?.active) return true;
      if (current.version && mod.version && foundry.utils.isNewerVersion(mod.version, current.version)) return true;
      return false;
    });
    if (hasMismatch) {
      new ModuleReviewDialog(activeModules).render(true);
    }
  }
  if (requiresReload) {
    foundry.applications.settings.SettingsConfig.reloadConfirm({ world: true });
  }
}
__name(importSettingsFromJSON, "importSettingsFromJSON");
var fetchSoundSettings = /* @__PURE__ */ __name(() => {
  midiSoundSettings = game.settings.get("midi-qol", "MidiSoundSettings") ?? {};
  if (midiSoundSettings.version === void 0) {
    midiSoundSettingsBackup = foundry.utils.duplicate(midiSoundSettings);
    midiSoundSettings = { "any": midiSoundSettings };
    midiSoundSettings.version = "0.9.48";
  }
}, "fetchSoundSettings");
var fetchParams = /* @__PURE__ */ __name(() => {
  if (debugEnabled > 1) debug("Fetch Params Loading");
  const blfxActive = game.modules.get("boss-loot-assets-premium")?.active || game.modules.get("boss-loot-assets-free")?.active;
  configSettings = game.settings.get("midi-qol", "ConfigSettings");
  if (configSettings.saveDROrder === void 0) configSettings.saveDROrder = "DRSaveDr";
  if (!["DRSaveDr", "SaveDRDr", "SaveDrDR"].includes(configSettings.saveDROrder)) {
    ui.notifications?.warn("invalidSaveDROrder reset to 'dm.midi/dnd5e.dm -> Saves/(dr/dv/di) - dnd5e RAW'");
    configSettings.saveDROrder = "DRSaveDr";
  }
  if (!configSettings.fumbleSound) configSettings.fumbleSound = CONFIG.sounds["dice"];
  if (!configSettings.criticalSound) configSettings.criticalSound = CONFIG.sounds["dice"];
  if (!configSettings.diceSound) configSettings.diceSound = CONFIG.sounds["dice"];
  if (!configSettings.doReactions) configSettings.doReactions = "none";
  if (!configSettings.gmDoReactions) configSettings.gmDoReactions = "none";
  if (configSettings.reactionTimeout === void 0) configSettings.reactionTimeout = 0;
  if (configSettings.convenientEffectsReaction === void 0) configSettings.convenientEffectsReaction = "Reaction";
  if (typeof configSettings.rangeTarget !== "string") configSettings.rangeTarget = "none";
  if (configSettings.showReactionAttackRoll === void 0) configSettings.showReactionAttackRoll = "all";
  if (configSettings.rollChecksBlind === true) configSettings.rollChecksBlind = ["all"];
  if (!configSettings.rollChecksBlind) configSettings.rollChecksBlind = [];
  configSettings.rollChecksBlind = configSettings.rollChecksBlind.filter((item) => item !== "none");
  if (configSettings.rollSavesBlind === true) configSettings.rollSavesBlind = ["all"];
  if (!configSettings.rollSavesBlind) configSettings.rollSavesBlind = [];
  configSettings.rollSavesBlind = configSettings.rollSavesBlind.filter((item) => item !== "none");
  if (configSettings.rollSkillsBlind === true) configSettings.rollSkillsBlind = ["all"];
  if (!configSettings.rollSkillsBlind) configSettings.rollSkillsBlind = [];
  configSettings.rollSkillsBlind = configSettings.rollSkillsBlind.filter((item) => item !== "none");
  migrateGmRollHiding(configSettings);
  if (configSettings.addFakeDice === void 0) configSettings.addFakeDice = false;
  if (typeof configSettings.gmConsumeResource !== "string") configSettings.gmConsumeResource = "none";
  if (typeof configSettings.consumeResource !== "string") configSettings.consumeResource = "none";
  if (!configSettings.enableDDBGL) configSettings.enableDDBGL = false;
  if (!configSettings.showReactionChatMessage) configSettings.showReactionChatMessage = false;
  if (configSettings.fixStickyKeys === void 0) configSettings.fixStickyKeys = true;
  if (configSettings.autoCEEffects === true) configSettings.autoCEEffects = "both";
  if (!configSettings.autoCEEffects) configSettings.autoCEEffects = "none";
  configSettings.toggleOptionalRules = false;
  if (configSettings.displaySaveAdvantage === void 0) configSettings.displaySaveAdvantage = true;
  if (!configSettings.recordAOO) configSettings.recordAOO = "none";
  if (!configSettings.enforceReactions) configSettings.enforceReactions = "none";
  if (!configSettings.enforceBonusActions) configSettings.enforceBonusActions = "none";
  if (configSettings.autoItemEffects === false) configSettings.autoItemEffects = "off";
  if (configSettings.playerDamageCard === void 0) configSettings.playerDamageCard = "none";
  if (configSettings.playerCardDamageDifferent === void 0) configSettings.playerCardDamageDifferent = true;
  if (configSettings.rollAlternate === void 0) configSettings.rollAlternate = "off";
  if (configSettings.rollAlternate === false) configSettings.rollAlternate = "off";
  if (configSettings.rollAlternate === true) configSettings.rollAlternate = "formula";
  if (configSettings.allowActorUseMacro === void 0) configSettings.allowActorUseMacro = configSettings.allowUseMacro;
  if (configSettings.skillAbilityCheckAdvantage === void 0) configSettings.skillAbilityCheckAdvantage = true;
  if (!configSettings.keyMapping || !configSettings.keyMapping["DND5E.Advantage"] || !configSettings.keyMapping["DND5E.Disadvantage"] || !configSettings.keyMapping["DND5E.Critical"]) {
    configSettings.keyMapping = defaultKeyMapping;
  }
  if (configSettings.addWounded === void 0) configSettings.addWounded = 0;
  if (configSettings.addWounded > 0 && configSettings.addWoundedStyle === void 0)
    configSettings.addWoundedStyle = "normal";
  if (!configSettings.addDead) configSettings.addDead = "none";
  if (typeof configSettings.addDead === "boolean" && configSettings.addDead) configSettings.addDead = "overlay";
  if (configSettings.paranoidGM === void 0) configSettings.paranoidGM = false;
  if (typeof configSettings.requiresTargets !== "string") configSettings.requiresTargets = "none";
  if (configSettings.tempHPDamageConcentrationCheck === void 0) configSettings.tempHPDamageConcentrationCheck = false;
  if (configSettings.showDSN === void 0) configSettings.showDSN = true;
  if (configSettings.showFastForward === void 0) configSettings.showFastForward = true;
  if (configSettings.highlightSuccess === void 0) configSettings.highlightSuccess = true;
  if (configSettings.highLightCriticalAttackOnly === void 0) configSettings.highLightCriticalAttackOnly = false;
  configSettings.optionalRules = foundry.utils.mergeObject({
    // eventually split this into mechanics and rules
    actionSpecialDurationImmediate: false,
    activeDefence: false,
    activeDefenceModifier: 12,
    // default value for active defence modifier
    activeDefenceShow: "selfroll",
    ActiveDefenceDC: true,
    challengeModeArmor: "none",
    challengeModeArmorScale: false,
    checkFlanking: "off",
    flankingBonus: "advantage",
    flankingRequiresVisibility: false,
    checkRange: "longfail",
    coverCalculation: "none",
    displayBonusRolls: true,
    criticalNat20: false,
    criticalSaves: false,
    distanceIncludesHeight: false,
    distanceMeasurement: "center",
    incapacitated: "nothing",
    invisAdvantage: "RAW2024",
    hiddenAdvantage: "none",
    maxDRValue: false,
    DRAllPerDamageDetail: true,
    nearbyAllyRanged: 0,
    nearbyFoe: 5,
    optionalCritRule: -1,
    removeHiddenInvis: true,
    wallsBlockRange: "center",
    mergeCardMulti: false,
    mergeCardMultiDamage: false,
    displayProperties: true
  }, configSettings.optionalRules ?? {}, { overwrite: true, insertKeys: true, insertValues: true });
  if (!configSettings.optionalRules.incapacitated) configSettings.optionalRules.incapacitated = "nothing";
  if (!configSettings.optionalRules.wallsBlockRange) configSettings.optionalRules.wallsBlockRange = "center";
  if (configSettings.optionalRules.checkFlanking === true) configSettings.optionalRules.checkFlanking = "ceadv";
  if (!configSettings.optionalRules.coverCalculation) configSettings.optionalRules.coverCalculation = "none";
  if (configSettings.optionalRules.displayBonusRolls === void 0) configSettings.optionalRules.displayBonusRolls = true;
  if (!configSettings.optionalRules.vitalityDeadCondition) configSettings.optionalRules.vitalityDeadCondition = "dead";
  if (!configSettings.optionalRules.vitalityDeathRequires) configSettings.optionalRules.vitalityDeathRequires = "vitalityOnly";
  if (configSettings.optionalRules.checkFlanking === false) configSettings.optionalRules.checkFlanking = "off";
  if (configSettings.optionalRules.checkRange === true) configSettings.optionalRules.checkRange = "longfail";
  if (!configSettings.optionalRules.checkRange) configSettings.optionalRules.checkRange = "none";
  if (!configSettings.optionalRules.invisAdvantage) configSettings.optionalRules.invisAdvantage = "none";
  if (configSettings.optionalRules.invisAdvantage === true) configSettings.optionalRules.invisAdvantage = "RAW2014";
  if (configSettings.optionalRules.invisAdvantage === "RAW") configSettings.optionalRules.invisAdvantage = "RAW2014";
  if (!configSettings.optionalRules.hiddenAdvantage) configSettings.optionalRules.hiddenAdvantage = "none";
  if (configSettings.optionalRules.activeDefenceShowGM === true) {
    configSettings.optionalRules.activeDefenceShow = "gmroll";
    delete configSettings.optionalRules.activeDefenceShowGM;
  }
  if (typeof configSettings.confirmAttackDamage !== "string") configSettings.confirmAttackDamage = "none";
  if (typeof configSettings.requireMagical !== "string" && configSettings.requireMagical !== true) configSettings.requireMagical = "off";
  if (typeof configSettings.requireMagical !== "string" && configSettings.requireMagical === true) configSettings.requireMagical = "nonspell";
  if (typeof configSettings.optionalRules.nearbyFoe !== "number") {
    if (configSettings.optionalRulesEnabled)
      configSettings.optionalRules.nearbyFoe = 5;
    else
      configSettings.optionalRules.nearbyFoe = 0;
  }
  const itemList = Object.keys(CONFIG.Item.typeLabels);
  if (!configSettings.itemTypeList && itemList.length > 0) {
    configSettings.itemTypeList = itemList;
    configSettings.itemTypeList.filter((type) => !["base", "backpack"].includes(type));
  }
  if (configSettings.itemTypeList === void 0) configSettings.itemTypeList = Object.keys(CONFIG.Item.typeLabels);
  if (configSettings.defaultSaveMultiplier === void 0) configSettings.defaultSaveMultiplier = 0.5;
  if (configSettings.ignoreSpellReactionRestriction === void 0) configSettings.ignoreSpellReactionRestriction = false;
  if (configSettings.damageImmunityMultiplier === void 0) configSettings.damageImmunityMultiplier = 0;
  if (configSettings.damageResistanceMultiplier === void 0) configSettings.damageResistanceMultiplier = 0.5;
  if (configSettings.damageVulnerabilityMultiplier === void 0) configSettings.damageVulnerabilityMultiplier = 2;
  if (configSettings.hidePlayerDamageCard === void 0) configSettings.hidePlayerDamageCard = true;
  if (configSettings.attackPerTarget === void 0) configSettings.attackPerTarget = false;
  if (configSettings.autoRemoveTemplate === void 0) configSettings.autoRemoveTemplate = true;
  if (configSettings.autoRemoveInstantaneousTemplate === void 0) configSettings.autoRemoveInstantaneousTemplate = false;
  if (configSettings.removeConcentrationEffects === void 0) configSettings.removeConcentrationEffects = "effects";
  if (configSettings.doConcentrationCheck === void 0) configSettings.doConcentrationCheck = configSettings.removeConcentration ? "chat" : "none";
  if (configSettings.undoWorkflow === void 0) configSettings.undoWorkflow = false;
  if (configSettings.undoChatColor === void 0) configSettings.undoChatColor = "Delete";
  if (configSettings.enforceSingleWeaponTarget == void 0) configSettings.enforceSingleWeaponTarget = false;
  if (configSettings.confirmAmmunition === void 0) configSettings.confirmAmmunition = false;
  if (configSettings.gmConfirmAmmunition === void 0) configSettings.gmConfirmAmmunition = false;
  if (configSettings.playerRollSaves === "noneDialog") configSettings.playerRollSaves = "noneDialogPrivate";
  configSettings.hidePlayerDamageCard = true;
  configSettings.quickSettings = true;
  if (configSettings.averageNPCDamage !== void 0) {
    delete configSettings.averageNPCDamage;
  }
  if (configSettings.averageDamage === void 0) configSettings.averageDamage = "none";
  enableWorkflow = game.settings.get("midi-qol", "EnableWorkflow");
  if (configSettings.optionalRules.challengeModeArmor === true) {
    if (configSettings.optionalRules.challengeModeArmorScale)
      configSettings.optionalRules.challengeModeArmor = "scale";
    else
      configSettings.optionalRules.challengeModeArmor = "challenge";
  } else if ([false, void 0].includes(configSettings.optionalRules.challengeModeArmor)) {
    configSettings.optionalRules.challengeModeArmor = "none";
  }
  if (configSettings.midiWoundedCondition === void 0) {
    configSettings.midiWoundedCondition = "none";
    configSettings.addWoundedStyle = "none";
  }
  if (configSettings.midiDeadCondition === void 0) configSettings.midiDeadCondition = "none";
  if (configSettings.midiUnconsciousCondition === void 0) configSettings.midiUnconsciousCondition = "none";
  if (configSettings.autoTarget === "wallsBlockIgnoreIncapcitated") configSettings.autoTarget = "wallsBlockIgnoreIncapacitated";
  if (configSettings.autoTarget === "wallsBlockIgnoreIncapacitated") configSettings.autoTarget = "wallsBlockIgnoreIncapacitated";
  if (configSettings.autoTarget === "alwaysIgnoreIncapcitated") configSettings.autoTarget = "alwaysIgnoreIncapacitated";
  configSettings.useDamageDetail = true;
  if (configSettings.collapsibleTargets === void 0) configSettings.collapsibleTargets = true;
  if (configSettings.displayProperties === void 0) configSettings.displayProperties = true;
  criticalDamage = configSettings.criticalDamage ?? "default";
  criticalDamageGM = configSettings.criticalDamageGM ?? criticalDamage;
  nsaFlag = game.settings.get("midi-qol", "showGM");
  coloredBorders = configSettings.coloredBorders ?? "none";
  itemRollButtons = false;
  if (configSettings.addChatDamageButtons === void 0) configSettings.addChatDamageButtons = "none";
  if (!fastForwardMigrationDone) {
    let needsFastForwardMigration = false;
    const oldFastForwardPlayer = typeof configSettings.autoFastForward === "string" ? configSettings.autoFastForward : void 0;
    const oldGmFfAttack = typeof configSettings.gmAutoFastForwardAttack === "boolean" ? configSettings.gmAutoFastForwardAttack : void 0;
    const oldGmFfDamage = typeof configSettings.gmAutoFastForwardDamage === "boolean" ? configSettings.gmAutoFastForwardDamage : void 0;
    let oldAbilityRolls;
    if (typeof configSettings.autoFastForward === "string") {
      const oldValue = configSettings.autoFastForward;
      const newValue = [];
      if (oldValue === "attack" || oldValue === "all") newValue.push("attack");
      if (oldValue === "damage" || oldValue === "all") newValue.push("damage");
      configSettings.autoFastForward = newValue;
      needsFastForwardMigration = true;
    }
    if (oldGmFfAttack !== void 0 || oldGmFfDamage !== void 0) {
      const newGmValue = [];
      if (oldGmFfAttack) newGmValue.push("attack");
      if (oldGmFfDamage) newGmValue.push("damage");
      configSettings.gmAutoFastForward = newGmValue;
      delete configSettings.gmAutoFastForwardAttack;
      delete configSettings.gmAutoFastForwardDamage;
      needsFastForwardMigration = true;
    }
    try {
      oldAbilityRolls = game.settings.get("midi-qol", "AutoFastForwardAbilityRolls");
      if (oldAbilityRolls) {
        for (const rollType of ["check", "save", "skill", "tool"]) {
          if (!configSettings.autoFastForward.includes(rollType)) configSettings.autoFastForward.push(rollType);
          if (!configSettings.gmAutoFastForward.includes(rollType)) configSettings.gmAutoFastForward.push(rollType);
        }
        needsFastForwardMigration = true;
      }
    } catch (e) {
    }
    if (needsFastForwardMigration) {
      console.warn(
        "midi-qol | Migrated fast-forward settings to granular arrays:",
        `
  Player autoFastForward: "${oldFastForwardPlayer ?? "(already migrated)"}" \u2192 [${configSettings.autoFastForward.join(", ")}]`,
        `
  GM gmAutoFastForwardAttack: ${oldGmFfAttack ?? "(already migrated)"}, gmAutoFastForwardDamage: ${oldGmFfDamage ?? "(already migrated)"} \u2192 gmAutoFastForward: [${configSettings.gmAutoFastForward.join(", ")}]`,
        `
  AutoFastForwardAbilityRolls: ${oldAbilityRolls ?? false}`
      );
      const persistMigration = /* @__PURE__ */ __name(() => {
        game.settings.set("midi-qol", "ConfigSettings", configSettings);
        if (oldAbilityRolls) game.settings.set("midi-qol", "AutoFastForwardAbilityRolls", false);
      }, "persistMigration");
      if (game.ready) persistMigration();
      else Hooks.once("ready", persistMigration);
    }
    fastForwardMigrationDone = true;
  }
  if (!Array.isArray(configSettings.autoFastForward)) configSettings.autoFastForward = [];
  if (!Array.isArray(configSettings.gmAutoFastForward)) configSettings.gmAutoFastForward = [];
  if (configSettings.preferredGM === void 0) {
    try {
      const oldPreferredGM = game.settings.get("midi-qol", "PreferredGM");
      if (oldPreferredGM && oldPreferredGM !== "") {
        configSettings.preferredGM = oldPreferredGM;
        console.warn(`midi-qol | Migrated PreferredGM setting "${oldPreferredGM}" into configSettings`);
        game.settings.set("midi-qol", "ConfigSettings", configSettings);
      } else {
        configSettings.preferredGM = "";
      }
    } catch (e) {
      configSettings.preferredGM = "";
    }
  }
  if (configSettings.criticalDamage === void 0) {
    try {
      const oldCriticalDamage = game.settings.get("midi-qol", "CriticalDamage");
      configSettings.criticalDamage = !oldCriticalDamage || oldCriticalDamage === "none" ? "default" : oldCriticalDamage;
      const oldCriticalDamageGM = game.settings.get("midi-qol", "CriticalDamageGM");
      configSettings.criticalDamageGM = !oldCriticalDamageGM || oldCriticalDamageGM === "none" ? configSettings.criticalDamage : oldCriticalDamageGM;
      console.warn(`midi-qol | Migrated CriticalDamage settings into configSettings (player: "${configSettings.criticalDamage}", GM: "${configSettings.criticalDamageGM}")`);
      game.settings.set("midi-qol", "ConfigSettings", configSettings);
    } catch (e) {
      configSettings.criticalDamage = "default";
      configSettings.criticalDamageGM = "default";
    }
  }
  if (configSettings.coloredBorders === void 0) {
    try {
      const oldColoredBorders = game.settings.get("midi-qol", "ColoredBorders");
      configSettings.coloredBorders = !oldColoredBorders || oldColoredBorders === "None" ? "none" : oldColoredBorders;
      console.warn(`midi-qol | Migrated ColoredBorders setting "${oldColoredBorders}" into configSettings as "${configSettings.coloredBorders}"`);
      game.settings.set("midi-qol", "ConfigSettings", configSettings);
    } catch (e) {
      configSettings.coloredBorders = "none";
    }
  }
  autoRemoveTargets = game.settings.get("midi-qol", "AutoRemoveTargets");
  if (autoRemoveTargets === "allGM") {
    autoRemoveTargets = game.user?.isGM ? "all" : "dead";
  }
  let debugText = game.settings.get("midi-qol", "Debug");
  forceHideRoll = game.settings.get("midi-qol", "ForceHideRoll");
  dragDropTargeting = game.settings.get("midi-qol", "DragDropTarget");
  debounceInterval = game.settings.get("midi-qol", "DebounceInterval");
  replaceDefaultActivities = game.settings.get("midi-qol", "ReplaceDefaultActivities");
  const oldWeakReferences = useWeakReferences;
  useWeakReferences = game.settings.get("midi-qol", "UseWeakReferences") ?? false;
  if (useWeakReferences !== oldWeakReferences) Workflow.clearWorkflows();
  saveToChatCard = game.settings.get("midi-qol", "SaveToChatCard");
  pruneChatLog = game.settings.get("midi-qol", "pruneChatLog");
  targetConfirmation = game.settings.get("midi-qol", "TargetConfirmation");
  if (configSettings.griddedGridless === void 0) configSettings.griddedGridless = false;
  if (configSettings.gridlessFudge === void 0) configSettings.gridlessFudge = 0;
  if (configSettings.concentrationIncapacitatedConditionCheck === void 0) configSettings.concentrationIncapacitatedConditionCheck = false;
  if (configSettings.activationAutomation === void 0) configSettings.activationAutomation = "chat";
  if (configSettings.activityNamePrefix === void 0) configSettings.activityNamePrefix = true;
  if (blfxActive && configSettings.activityNamePrefix) {
    TroubleShooter.recordError({}, "Boss Loot FX is active - disabling activity name prefix");
    console.warn("midiqol | Boss Loot FX is active - disabling activity name prefix");
    configSettings.activityNamePrefix = false;
  }
  if (targetConfirmation === void 0 || typeof targetConfirmation === "string" || targetConfirmation instanceof String) targetConfirmation = {
    enabled: false,
    always: false,
    noneTargeted: false,
    hasAttack: false,
    hasCreatureTarget: false,
    targetSelf: false,
    hasAoE: false,
    hasRangedAoE: false,
    longRange: false,
    inCover: false,
    allies: false,
    mixedDisposition: false,
    gridPosition: { x: 0, y: 0 }
  };
  setDebugLevel(debugText);
  if (configSettings.concentrationAutomation && game.user === game.users?.activeGM) {
    Hooks.once("ready", () => {
      if (configSettings.concentrationAutomation) {
        ui.notifications?.warn(`Midi concentration automation is deprecated in favour of ${game.system?.title} concentration checking - settings updated to use ${game.system?.id} concentration`, { permanent: true });
      }
      ;
      configSettings.concentrationAutomation = false;
      game.settings.set("dnd5e", "disableConcentration", false);
      game.settings.set("midi-qol", "ConfigSettings", configSettings);
    });
  }
  if (configSettings.displaySaveDC !== void 0 && game.user === game.users?.activeGM) {
    Hooks.once("ready", async () => {
      await migrateDisplaySaveDC(configSettings, "startup");
      await game.settings.set("midi-qol", "ConfigSettings", configSettings);
    });
  }
  updateFlankingStatusEffects();
  Hooks.callAll("midi-qol.ConfigSettingsChanged");
}, "fetchParams");
function readySettingsSetup() {
  game.settings.register("midi-qol", "PreferredGM", {
    name: "midi-qol.PreferredGM.Name",
    hint: "midi-qol.PreferredGM.Hint",
    scope: "world",
    default: "",
    config: false,
    type: String
  });
}
__name(readySettingsSetup, "readySettingsSetup");
function keysToOptionsObj(prefix, keys) {
  return Object.fromEntries(keys.map((key) => [key, `midi-qol.${prefix}.${key}`]));
}
__name(keysToOptionsObj, "keysToOptionsObj");
function keysToExtendedOptionsObj(prefix, keys) {
  return Object.fromEntries(keys.map((key) => [key, {
    label: `midi-qol.${prefix}.${key}`,
    hint: `midi-qol.${prefix}.expandedHints.${key}`
  }]));
}
__name(keysToExtendedOptionsObj, "keysToExtendedOptionsObj");
var criticalDamageChoices = keysToOptionsObj("CriticalDamageChoices", [
  "default",
  "maxDamage",
  "maxCrit",
  "maxCritRoll",
  "maxAll",
  "doubleDice",
  "explode",
  "maxDamageExplode",
  "explodeCharacter",
  "explodeNPC",
  "baseDamage",
  "maxBaseRollCrit",
  "bestOfTwo"
]);
var addChatDamageButtonsOptions = keysToExtendedOptionsObj("AddChatDamageButtonsOptions", [
  "none",
  "gm",
  "pc",
  "both"
]);
var coloredBordersOptions = keysToOptionsObj("ColoredBordersOptions", [
  "none",
  "borders",
  "borderNamesText",
  "borderNamesBackground"
]);
var autoRemoveTargetsOptions = keysToOptionsObj("AutoRemoveTargetsOptions", [
  "none",
  "dead",
  "allGM",
  "all"
]);
var debugOptions = keysToOptionsObj("DebugOptions", [
  "none",
  "warn",
  "debug",
  "all"
]);
var autoRollDamageOptions = keysToExtendedOptionsObj("autoRollDamageOptions", [
  "none",
  "always",
  "saveOnly",
  "onHit"
]);
var autoCheckHitOptions = keysToExtendedOptionsObj("autoCheckHitOptions", [
  "none",
  "all",
  "whisper",
  "gmOnly"
]);
var clickOptions = keysToExtendedOptionsObj("clickOptions", [
  "off",
  "attack",
  "damage",
  "all"
]);
var autoTargetOptions = keysToExtendedOptionsObj("autoTargetOptions", [
  "none",
  "always",
  "alwaysIgnoreIncapacitated",
  "alwaysIgnoreDefeated",
  "wallsBlock",
  "wallsBlockIgnoreIncapacitated",
  "wallsBlockIgnoreDefeated",
  "walledtemplates"
]);
var autoCheckSavesOptions = keysToExtendedOptionsObj("autoCheckSavesOptions", [
  "none",
  "allNoRoll",
  "all",
  "allShow",
  "whisper",
  "gmOnly"
]);
var rangeTargetOptions = keysToExtendedOptionsObj("rangeTargetOptions", [
  "none",
  "always",
  "alwaysIgnoreIncapacitated",
  "alwaysIgnoreDefeated",
  "wallsBlock",
  "wallsBlockIgnoreIncapacitated",
  "wallsBlockIgnoreDefeated"
]);
var autoApplyDamageOptions = keysToExtendedOptionsObj("autoApplyDamageOptions", [
  "none",
  "noCard",
  "noCardMisses",
  "yes",
  "yesCard",
  "yesCardMisses",
  "yesCardNPC"
]);
var damageCardStyleOptions = keysToExtendedOptionsObj("damageCardStyleOptions", [
  "flexbox",
  "classic"
]);
var playerDamageCardOptions = keysToExtendedOptionsObj("playerDamageCardOptions", [
  "none",
  "playerresults",
  "playerbuttons",
  "npcplayerresults",
  "npcplayerbuttons"
]);
var removeButtonsOptions = keysToOptionsObj("removeButtonsOptions", [
  "off",
  "attack",
  "damage",
  "all",
  "everything"
]);
var damageImmunitiesOptions = keysToExtendedOptionsObj("damageImmunitiesOptions", [
  "none",
  "immunityDefault",
  "immunityPhysical"
]);
var showItemDetailsOptions = keysToExtendedOptionsObj("showItemDetailsOptions", [
  "none",
  "cardOnly",
  "pc",
  "all"
]);
var rollNPCSavesOptions = keysToExtendedOptionsObj("rollNPCSavesOptions", [
  "none",
  "auto",
  "autoDialog",
  "mtb",
  "ftb",
  "rer"
]);
var playerRollSavesOptions = keysToExtendedOptionsObj("playerRollSavesOptions", [
  "none",
  "nonePublic",
  "noneDialogPublic",
  "noneDialogPrivate",
  "noneDialogSelf",
  "noneDialogBlind",
  "chat",
  "mtb",
  "ftb",
  "rer"
]);
var playerRollSavesOptionsReduced2 = keysToExtendedOptionsObj("playerRollSavesOptionsReduced", [
  "none",
  "chat"
]);
var hideRollDetailsOptions = keysToExtendedOptionsObj("hideRollDetailsOptions", [
  "none",
  "detailsDSN",
  "details",
  "d20Only",
  "d20DamageDSN",
  "hitDamage",
  "hitCriticalDamage",
  "attackTotalOnly",
  "d20AttackOnly",
  "all"
]);
var gmAttackDisplayOptions = keysToExtendedOptionsObj("gmAttackDisplayOptions", [
  "full",
  "hideFormula",
  "d20Only",
  "attackTotal",
  "hitMiss",
  "hitMissCrit",
  "hidden"
]);
var gmDamageDisplayOptions = keysToExtendedOptionsObj("gmDamageDisplayOptions", [
  "full",
  "totalOnly",
  "hidden"
]);
var gmDsnDisplayOptions = keysToExtendedOptionsObj("gmDsnDisplayOptions", [
  "show",
  "ghost",
  "none"
]);
var rollOtherDamageOptions = keysToExtendedOptionsObj("RollOtherDamageOptions", [
  "none",
  "always",
  "condition",
  "ifSave"
]);
var showReactionAttackRollOptions = keysToExtendedOptionsObj("ShowReactionAttackRollOptions", [
  "none",
  "d20",
  "all",
  "allCrit"
]);
var onUseMacroOptions = keysToExtendedOptionsObj("onUseMacroOptions", [
  "preTargeting",
  "preItemRoll",
  "templatePlaced",
  "preAttackRoll",
  "preAttackRollConfig",
  "preCheckHits",
  "postAttackRoll",
  "preDamageRoll",
  "postDamageRoll",
  "damageBonus",
  "preSave",
  "postSave",
  "preDamageRollConfig",
  "preDamageApplication",
  "preActiveEffects",
  "postActiveEffects",
  "isTargeted",
  "isPreAttacked",
  "isAttacked",
  "isHit",
  "preTargetSave",
  "isSave",
  "isSaveSuccess",
  "isSaveFailure",
  "preTargetDamageApplication",
  "postTargetEffectApplication",
  "isDamaged",
  "all"
]);
var confirmTargetOptions = keysToOptionsObj("ConfirmTargetOptions", [
  "default",
  "always",
  "never"
]);
var triggeredActivityTargetOptions = keysToOptionsObj("TriggeredActivityTargetOptions", [
  "self",
  "targets",
  "hitTargets",
  "missedTargets",
  "failedSaves",
  "saveTargets",
  "retarget"
]);
var triggeredActivityRollAsOptions = keysToOptionsObj("TriggeredActivityRollAsOptions", [
  "self",
  "firstTarget",
  "firstHitTarget",
  "firstMissedTarget",
  "firstSaveTarget",
  "firstFailedSaveTarget"
]);
var ignoreTraitsOptionsBase = {
  idi: {
    label: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idi.label",
    hint: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idi.hint"
  },
  idr: {
    label: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idr.label",
    hint: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idr.hint"
  },
  idv: {
    label: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idv.label",
    hint: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idv.hint"
  },
  ida: {
    label: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.ida.label",
    hint: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.ida.hint"
  },
  idm: {
    label: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idm.label",
    hint: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idm.hint"
  }
};
var ignoreTraitsCategoryLabels = {
  idi: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idi.short",
  idr: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idr.short",
  idv: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idv.short",
  ida: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.ida.short",
  idm: "midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.idm.short"
};
function getIgnoreTraitsOptions() {
  const options = [];
  const damageTypes = CONFIG.DND5E?.damageTypes ?? {};
  for (const [key, entry] of Object.entries(ignoreTraitsOptionsBase)) {
    options.push({
      value: key,
      label: i18n(entry.label),
      group: i18n("midi-qol.SHARED.FIELDS.midiProperties.ignoreTraits.allTypes")
    });
  }
  for (const [category, categoryEntry] of Object.entries(ignoreTraitsOptionsBase)) {
    const categoryLabel = i18n(ignoreTraitsCategoryLabels[category] ?? categoryEntry.label);
    for (const [dt, dtConfig] of Object.entries(damageTypes)) {
      const dtLabel = dtConfig.label ?? dt;
      options.push({
        value: `${category}.${dt}`,
        label: `${categoryLabel}: ${dtLabel}`,
        group: categoryLabel
      });
    }
  }
  return options;
}
__name(getIgnoreTraitsOptions, "getIgnoreTraitsOptions");
var aoeTargetTypeOptions = keysToOptionsObj("AoETargetTypeOptions", [
  "any",
  "ally",
  "notAlly",
  "enemy",
  "notEnemy",
  "neutral",
  "notNeutral",
  "hostile",
  "notHostile",
  "Friendly",
  //grr
  "notFriendly"
]);
var autoCEEffectsOptions = keysToExtendedOptionsObj("AutoCEEffectsOptions", [
  "none",
  "itempri",
  "cepri",
  "both"
]);
var wallsBlockRangeOptionsNew = keysToExtendedOptionsObj("WallsBlockRangeOptionsNew", [
  "none",
  "center",
  "centerLevels",
  "levelsautocover",
  "simbuls-cover-calculator",
  "tokencover",
  "simplecover5e"
]);
var coverCalculationOptions = keysToExtendedOptionsObj("CoverCalculationOptions", [
  "none",
  "simbuls-cover-calculator",
  "simplecover5e",
  "levelsautocover",
  "tokencover"
]);
var hiddenAdvantageOptions = keysToExtendedOptionsObj("HiddenAdvantageOptions", [
  "none",
  "effect",
  "perceptive"
]);
var addDeadOptions = keysToExtendedOptionsObj("AddDeadOptions", [
  "none",
  "overlay",
  "normal"
]);
var requiresTargetsOptions = keysToExtendedOptionsObj("requiresTargetsOptions", [
  "none",
  "combat",
  "tokens",
  "always"
]);
var doReactionsOptions = keysToExtendedOptionsObj("DoReactionsOptions", [
  "all",
  "allMI",
  "none"
]);
var gmDoReactionsOptions = keysToExtendedOptionsObj("GMDoReactionsOptions", [
  "all",
  "allMI",
  "none"
]);
var recordAOOOptions = keysToExtendedOptionsObj("RecordAOOOptions", [
  "none",
  "character",
  "all"
]);
var EnforceReactionsOptions = keysToExtendedOptionsObj("EnforceReactionsOptions", [
  "none",
  "displayOnly",
  "character",
  "all"
]);
var autoEffectsOptions = keysToExtendedOptionsObj("AutoEffectsOptions", [
  "off",
  "applyNoButton",
  "applyRemove",
  "applyLeave"
]);
var requireMagicalOptions = keysToExtendedOptionsObj("RequireMagicalOptions", [
  "off",
  "nonspell",
  "allitems"
]);
var checkFlankingOptions = keysToExtendedOptionsObj("CheckFlankingOptions", [
  "off",
  "advonly",
  "ceonly",
  "midiFlanking",
  "ceflanked",
  "midiFlanked",
  "ceflankedNoconga",
  "midiFlankedNoConga"
]);
var flankingBonusOptions = {
  "advantage": "midi-qol.FlankingBonusOptions.advantage",
  "1": "midi-qol.FlankingBonusOptions.1",
  "2": "midi-qol.FlankingBonusOptions.2",
  "3": "midi-qol.FlankingBonusOptions.3",
  "4": "midi-qol.FlankingBonusOptions.4",
  "5": "midi-qol.FlankingBonusOptions.5"
};
var rollAlternateOptions = keysToExtendedOptionsObj("RollAlternateOptions", [
  "off",
  "formula",
  "adv",
  "formulaadv"
]);
var consumeResourceOptions = keysToExtendedOptionsObj("ConsumeResourceOptions", [
  "none",
  "spell",
  "item",
  "both"
]);
var averageDamageOptions = keysToExtendedOptionsObj("AverageDamageOptions", [
  "none",
  "npc",
  "character",
  "all"
]);
var removeConcentrationEffectsOptions = keysToExtendedOptionsObj("RemoveConcentrationEffectsOptions", [
  "none",
  "effects",
  "effectsTemplates"
]);
var incapacitatedOptions = keysToExtendedOptionsObj("IncapacitatedOptions", [
  "nothing",
  "enforce",
  "warn"
]);
var checkRangeOptions = keysToExtendedOptionsObj("CheckRangeOptions", [
  "none",
  "longfail",
  "longdisadv"
]);
var distanceMeasurementOptions = keysToExtendedOptionsObj("DistanceMeasurementOptions", [
  "default",
  "center",
  "perimeter"
]);
var invisAdvantageOptions = keysToExtendedOptionsObj("InvisAdvantageOptions", [
  "none",
  "RAW2014",
  "RAW2024",
  "vision"
]);
var confirmAttackDamageOptions = keysToExtendedOptionsObj("ConfirmAttackDamageOptions", [
  "none",
  "gmOnly",
  "GMPlayer"
]);
var challengeModeArmorOptions = keysToExtendedOptionsObj("ChallengeModeArmorOptions", [
  "none",
  "challenge",
  "scale",
  "scaleNoAR"
]);
var saveDROrderOptions = keysToExtendedOptionsObj("SaveDROrderOptions", [
  "DRSaveDr",
  "SaveDRDr",
  "SaveDrDR"
]);
var doConcentrationCheckOptions = keysToExtendedOptionsObj("DoConcentrationCheckOptions", [
  "none",
  "chatOnly",
  "chat",
  "item"
]);
var activationAutomationOptions = keysToOptionsObj("ActivationAutomationOptions", [
  "none",
  "chat",
  "auto"
]);
var rollConfigOptions = keysToOptionsObj("ConfigDialogOptions", [
  "default",
  "always",
  "never"
]);
var damageConfigOptions = keysToOptionsObj("ConfigDialogOptions", [
  "default",
  "always",
  "never"
]);
var consumeConfigOptions = keysToOptionsObj("ConfigDialogOptions", [
  "default",
  "always",
  "never"
]);
var registerSettings = /* @__PURE__ */ __name(function() {
  game.settings.register("midi-qol", "ConfigSettings", {
    scope: "world",
    // @ts-expect-error Object type is valid for Foundry settings
    type: Object,
    default: configSettings,
    onChange: fetchParams,
    config: false
  });
  game.settings.register("midi-qol", "MidiSoundSettings", {
    scope: "world",
    type: Object,
    default: midiSoundSettings,
    onChange: fetchSoundSettings,
    config: false
  });
  game.settings.register("midi-qol", "MidiSoundSettings-backup", {
    scope: "world",
    type: Object,
    default: {},
    config: false
  });
  game.settings.register("midi-qol", "RollStats", {
    scope: "world",
    default: {},
    type: Object,
    config: false,
    onChange: /* @__PURE__ */ __name(() => {
      if (!game.user?.isGM) {
        gameStats.fetchStats();
      }
      Hooks.callAll("midi-qol.StatsUpdated");
    }, "onChange")
  });
  game.settings.register("midi-qol", "TargetConfirmation", {
    scope: "client",
    type: Object,
    default: defaultTargetConfirmationSettings,
    config: false,
    onChange: fetchParams
  });
  game.settings.register("midi-qol", "notificationVersion", {
    scope: "world",
    default: "0.0.0",
    type: String,
    config: false
  });
  game.settings.register("midi-qol", "splashWarnings", {
    scope: "world",
    type: Boolean,
    config: false,
    default: true
  });
  game.settings.register("midi-qol", "last-run-version", {
    type: String,
    config: false,
    default: "0.0.0",
    requiresReload: true
  });
  game.settings.register("midi-qol", "instanceId", {
    type: String,
    config: false,
    default: "",
    requiresReload: true
  });
  game.settings.register("midi-qol", "EnableWorkflow", {
    name: i18n("midi-qol.EnableWorkflow.Name"),
    hint: i18n("midi-qol.EnableWorkflow.Hint"),
    scope: "client",
    default: true,
    config: true,
    type: Boolean,
    onChange: fetchParams
  });
  game.settings.register("midi-qol", "PreferredGM", {
    name: i18n("midi-qol.PreferredGM.Name"),
    hint: i18n("midi-qol.PreferredGM.Hint"),
    scope: "world",
    default: "",
    config: false,
    type: String
  });
  game.settings.register("midi-qol", "ReplaceDefaultActivities", {
    name: i18n("midi-qol.ReplaceDefaultActivities.Name"),
    hint: i18n("midi-qol.ReplaceDefaultActivities.Hint"),
    scope: "world",
    default: true,
    config: true,
    type: Boolean,
    requiresReload: true
  });
  game.settings.register("midi-qol", "AutoFastForwardAbilityRolls", {
    name: i18n("midi-qol.AutoFastForwardAbilityRolls.Name"),
    hint: i18n("midi-qol.AutoFastForwardAbilityRolls.Hint"),
    scope: "world",
    default: false,
    type: Boolean,
    config: false
  });
  game.settings.register("midi-qol", "showGM", {
    name: i18n("midi-qol.showGM.Name"),
    hint: i18n("midi-qol.showGM.Hint"),
    scope: "world",
    default: false,
    type: Boolean,
    config: true,
    onChange: fetchParams
  });
  game.settings.register("midi-qol", "ForceHideRoll", {
    name: i18n("midi-qol.ForceHideRoll.Name"),
    hint: i18n("midi-qol.ForceHideRoll.Hint"),
    scope: "client",
    default: true,
    type: Boolean,
    config: true,
    onChange: fetchParams,
    requiresReload: true
  });
  game.settings.register("midi-qol", "DragDropTarget", {
    name: i18n("midi-qol.DragDropTarget.Name"),
    hint: i18n("midi-qol.DragDropTarget.Hint"),
    scope: "world",
    default: false,
    type: Boolean,
    onChange: fetchParams,
    config: true
  });
  game.settings.register("midi-qol", "CriticalDamageGM", {
    name: "midi-qol.CriticalDamageGM.Name",
    scope: "world",
    default: "none",
    type: String,
    config: false,
    choices: criticalDamageChoices,
    onChange: fetchParams
  });
  game.settings.register("midi-qol", "CriticalDamage", {
    name: "midi-qol.CriticalDamage.Name",
    scope: "world",
    default: "default",
    type: String,
    config: false,
    choices: criticalDamageChoices,
    onChange: fetchParams
  });
  game.settings.register(
    "midi-qol",
    "ColoredBorders",
    {
      name: "midi-qol.ColoredBorders.Name",
      hint: "midi-qol.ColoredBorders.Hint",
      scope: "world",
      default: "None",
      type: String,
      config: false,
      choices: coloredBordersOptions,
      onChange: fetchParams
    }
  );
  game.settings.register("midi-qol", "AutoRemoveTargets", {
    name: "midi-qol.AutoRemoveTargets.Name",
    hint: "midi-qol.AutoRemoveTargets.Hint",
    scope: "client",
    default: "dead",
    type: String,
    config: true,
    choices: autoRemoveTargetsOptions,
    onChange: fetchParams
  });
  game.settings.registerMenu("midi-qol", "midi-qol", {
    name: i18n("midi-qol.config"),
    label: "midi-qol.WorkflowSettings",
    hint: i18n("midi-qol.Hint"),
    icon: "fas fa-dice-d20",
    type: ConfigPanel,
    restricted: true
  });
  game.settings.registerMenu("midi-qol", "resetSettings", {
    name: i18n("midi-qol.ResetDefaults.Name"),
    label: "midi-qol.ResetDefaults.Button",
    hint: i18n("midi-qol.ResetDefaults.Hint"),
    icon: "fas fa-triangle-exclamation",
    type: ResetSettingsSubmenu,
    restricted: true
  });
  if (game.modules.get("midi-qol-setup-wizard")?.active) {
    game.settings.registerMenu("midi-qol", "launchSetupWizard", {
      name: i18n("midi-qol.LaunchSetupWizard.Name"),
      label: "midi-qol.LaunchSetupWizard.Label",
      hint: i18n("midi-qol.LaunchSetupWizard.Hint"),
      icon: "fas fa-magic",
      type: LaunchWizardSubmenu,
      restricted: true
    });
  }
  game.settings.registerMenu("midi-qol", "TargetConfirmationConfig", {
    name: i18n("midi-qol.TargetConfirmationConfig.Name"),
    label: i18n("midi-qol.TargetConfirmationConfig.Name"),
    hint: i18n("midi-qol.TargetConfirmationConfig.Hint"),
    icon: "fas fa-dice-d20",
    type: TargetConfirmationConfig,
    restricted: false
  });
  game.settings.registerMenu("midi-qol", "midi-qol-sounds", {
    name: i18n("midi-qol.SoundSettings.Name"),
    label: "midi-qol.SoundSettings.Label",
    hint: i18n("midi-qol.SoundSettings.Hint"),
    icon: "fas fa-dice-d20",
    type: SoundConfigPanel,
    restricted: true
  });
  game.settings.registerMenu("midi-qol", "showRollStats", {
    name: i18n("midi-qol.ShowStats"),
    label: "midi-qol.ShowStats",
    hint: i18n("midi-qol.KeepRollStats.Hint"),
    icon: "fas fa-chart-bar",
    type: RollStatsDisplay,
    restricted: false
  });
  game.settings.register("midi-qol", "playerControlsInvisibleTokens", {
    name: i18n("midi-qol.playerControlsInvisibleTokens.Name"),
    hint: i18n("midi-qol.playerControlsInvisibleTokens.Hint"),
    scope: "world",
    default: false,
    config: true,
    type: Boolean,
    requiresReload: true
  });
  game.settings.register("midi-qol", "pruneChatLog", {
    name: "Prune Chat Log",
    hint: "Automatically prune chat messages to limit those in memory.",
    scope: "world",
    default: true,
    type: Boolean,
    config: true,
    requiresReload: true,
    onChange: /* @__PURE__ */ __name((value) => {
      pruneChatLog = value;
      syncChatlogPruneSetting(value);
    }, "onChange")
  });
  game.settings.register("midi-qol", "DebounceInterval", {
    name: "Chat Message Cache Time (ms)",
    hint: "Chat message updates will only happen this often",
    scope: "world",
    default: 0,
    type: Number,
    config: true,
    onChange: /* @__PURE__ */ __name(() => {
      debounceInterval = game.settings.get("midi-qol", "DebounceInterval");
    }, "onChange")
  });
  game.settings.register("midi-qol", "Debug", {
    name: "midi-qol.Debug.Name",
    hint: "midi-qol.Debug.Hint",
    scope: "world",
    default: "None",
    type: String,
    config: true,
    choices: debugOptions,
    onChange: fetchParams
  });
  game.settings.register("midi-qol", "SaveToChatCard", {
    name: "Save to Chat Card",
    hint: "save the workflow state to the chat card - so that it can be restored on reload",
    scope: "world",
    default: false,
    type: Boolean,
    config: true,
    onChange: fetchParams
  });
  game.settings.register("midi-qol", "UseWeakReferences", {
    name: "Use Weak References for Workflows",
    hint: "Only enable if Save to Chat Card is enabled. This will use WeakReferences to store workflows - should save some memory.",
    scope: "world",
    default: false,
    type: Boolean,
    config: true,
    onChange: fetchParams
  });
  game.settings.registerMenu("midi-qol", "dependentsExplorer", {
    name: i18n("midi-qol.DependentsExplorer.Name"),
    label: "midi-qol.DependentsExplorer.Label",
    icon: "fas fa-sitemap",
    hint: i18n("midi-qol.DependentsExplorer.Hint"),
    type: DependentsExplorer,
    restricted: true
  });
  Hooks.on("renderSettingsConfig", (app, html) => {
    if (game.user?.isGM) return;
    const element = html instanceof HTMLElement ? html : html[0];
    if (!element) return;
    const menuButton = element.querySelector('[data-key="midi-qol.dependentsExplorer"]');
    if (menuButton) {
      const formGroup = menuButton.closest(".form-group");
      if (formGroup) {
        formGroup.style.display = "none";
      }
    }
  });
  game.settings.registerMenu("midi-qol", "troubleShooter", {
    name: i18n("midi-qol.TroubleShooter.Name"),
    label: "midi-qol.TroubleShooter.Label",
    icon: "fas fa-wrench",
    hint: i18n("midi-qol.TroubleShooter.Hint"),
    // icon: "fas fa-dice-d20",
    type: TroubleShooter,
    restricted: false
  });
}, "registerSettings");
function safeGetGameSetting(moduleName, settingName) {
  if (game.settings.settings.get(`${moduleName}.${settingName}`))
    return game.settings.get(moduleName, settingName);
  else return void 0;
}
__name(safeGetGameSetting, "safeGetGameSetting");
function shouldDisplaySaveDC(actor) {
  const visibility = safeGetGameSetting("dnd5e", "challengeVisibility");
  if (visibility === "none") return false;
  if (visibility === "all") return true;
  return !actor || actor.hasPlayerOwner;
}
__name(shouldDisplaySaveDC, "shouldDisplaySaveDC");

// src/module/preloadTemplates.ts
async function preloadTemplates() {
  const templatePaths = [
    "modules/midi-qol/templates/activity-card.hbs",
    "modules/midi-qol/templates/attack-roll.html",
    "modules/midi-qol/templates/bonus-damage-roll.html",
    "modules/midi-qol/templates/damage-results.html",
    "modules/midi-qol/templates/damage-results-classic.html",
    "modules/midi-qol/templates/damage-results-player.html",
    "modules/midi-qol/templates/damage-results-player-classic.html",
    "modules/midi-qol/templates/damage-roll-dialog.hbs",
    "modules/midi-qol/templates/damage-roll.html",
    "modules/midi-qol/templates/dialog.hbs",
    "modules/midi-qol/templates/hits.html",
    "modules/midi-qol/templates/other-damage-roll.html",
    "modules/midi-qol/templates/roll-base.html",
    "modules/midi-qol/templates/roll-dialog.hbs",
    "modules/midi-qol/templates/roll.html",
    // "modules/midi-qol/templates/roll-options.html",
    "modules/midi-qol/templates/saves.html",
    "modules/midi-qol/templates/tool-card.hbs",
    "modules/midi-qol/templates/tooltip.html",
    "modules/midi-qol/templates/activity/attack-effect.hbs",
    "modules/midi-qol/templates/activity/check-effect.hbs",
    "modules/midi-qol/templates/activity/damage-effect.hbs",
    "modules/midi-qol/templates/activity/enchant-effect.hbs",
    "modules/midi-qol/templates/activity/heal-effect.hbs",
    "modules/midi-qol/templates/activity/save-effect.hbs",
    "modules/midi-qol/templates/activity/utility-effect.hbs",
    "modules/midi-qol/templates/activity/parts/attack-details.hbs",
    "modules/midi-qol/templates/activity/parts/attack-extras.hbs",
    "modules/midi-qol/templates/activity/parts/midi-activity-tab.hbs",
    "modules/midi-qol/templates/activity/parts/save-damage.hbs",
    "modules/midi-qol/templates/activity/parts/save-extras.hbs",
    "modules/midi-qol/templates/activity/parts/overtime-extras.hbs",
    "modules/midi-qol/templates/apps/ActorOnUseMacroConfig/actorOnUseMacrosConfig.hbs",
    "modules/midi-qol/templates/apps/ConditionEditor/conditionEditor.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/concentration.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/footer.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/gm.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/mechanics.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/misc.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/player.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/quick.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/reactions.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/rules.hbs",
    "modules/midi-qol/templates/apps/ConfigPanel/workflow.hbs",
    "modules/midi-qol/templates/apps/DependentsExplorer/dependentsExplorer.hbs",
    "modules/midi-qol/templates/apps/ItemOnUseMacrosConfig/itemOnUseMacrosConfig.hbs",
    "modules/midi-qol/templates/apps/OverTimeEditor/overTimeEditor.hbs",
    "modules/midi-qol/templates/apps/RollStatsDisplay/config.hbs",
    "modules/midi-qol/templates/apps/RollStatsDisplay/stats.hbs",
    "modules/midi-qol/templates/apps/SoundConfigPanel/quick.hbs",
    "modules/midi-qol/templates/apps/SoundConfigPanel/sounds.hbs",
    "modules/midi-qol/templates/apps/TargetConfirmation/targetConfirmation.hbs",
    "modules/midi-qol/templates/apps/TargetConfirmationConfig/targetConfirmationConfig.hbs",
    "modules/midi-qol/templates/apps/ModuleReviewDialog/moduleReview.hbs",
    "modules/midi-qol/templates/apps/TroubleShooter/common.hbs",
    "modules/midi-qol/templates/apps/TroubleShooter/errors.hbs",
    "modules/midi-qol/templates/apps/TroubleShooter/modules.hbs",
    "modules/midi-qol/templates/apps/TroubleShooter/problems.hbs",
    "modules/midi-qol/templates/apps/TroubleShooter/summary.hbs",
    "modules/midi-qol/templates/apps/UndoWorkflow/undo-workflow.hbs"
  ];
  return foundry.applications.handlebars.loadTemplates(templatePaths);
}
__name(preloadTemplates, "preloadTemplates");

// src/module/sheetQOL.ts
var SUPPORTED_SHEETS = [
  "CharacterActorSheet",
  "NPCActorSheet"
];
function setupSheetQol() {
  for (const sheetName of SUPPORTED_SHEETS) {
    const hookName = `render${sheetName}`;
    if (debugEnabled > 0) debug("Registering sheetQOL hook:", hookName);
    Hooks.on(hookName, injectMidiButtons);
  }
}
__name(setupSheetQol, "setupSheetQol");
function injectMidiButtons(app, html, context, options) {
  if (!configSettings.addSheetButtons) return;
  const element = html instanceof HTMLElement ? html : html[0];
  if (!element) return;
  if (debugEnabled > 0) debug("injectMidiButtons called for", app.constructor.name);
  element.querySelectorAll(".item.collapsible > .item-row").forEach((itemRow) => {
    const itemElement = itemRow.closest(".item");
    const itemId = itemElement?.dataset.itemId;
    const item = app.document?.items?.get(itemId) ?? app.actor?.items?.get(itemId);
    if (!item) return;
    injectInfoIcon(itemRow, item);
  });
  element.querySelectorAll(".item.collapsible .item-description .wrapper").forEach((wrapper) => {
    const itemElement = wrapper.closest(".item");
    const itemId = itemElement?.dataset.itemId;
    const item = app.document?.items?.get(itemId) ?? app.actor?.items?.get(itemId);
    if (!item) return;
    wrapper.querySelectorAll(".activity-row").forEach((activityRow) => {
      const activityId = activityRow.dataset.activityId;
      const activity = item.system.activities?.get(activityId);
      if (!activity) return;
      injectActivityButtons(activityRow, item, activity);
    });
  });
  attachMidiButtonHandlers(element, app);
}
__name(injectMidiButtons, "injectMidiButtons");
function injectInfoIcon(itemRow, item) {
  if (itemRow.querySelector(".midi-info-icon")) return;
  const itemName = itemRow.querySelector(".item-name");
  if (!itemName) return;
  const infoHtml = `<a class="midi-info-icon" data-midi-action="info" data-item-id="${item.id}" data-tooltip="${i18n("midi-qol.buttons.showChatInfo")}"><i class="fas fa-info-circle"></i></a>`;
  const itemImage = itemName.querySelector(".item-image");
  if (itemImage) {
    itemImage.insertAdjacentHTML("afterend", infoHtml);
  } else {
    itemName.insertAdjacentHTML("afterbegin", infoHtml);
  }
}
__name(injectInfoIcon, "injectInfoIcon");
function injectActivityButtons(activityRow, item, activity) {
  if (activityRow.querySelector(".midi-activity-buttons")) return;
  const buttons = [];
  const itemId = item.id;
  const activityId = activity.id;
  if (activity.hasAttack) {
    buttons.push(`
      <button type="button" data-midi-action="attack" data-item-id="${itemId}" data-activity-id="${activityId}"
              data-tooltip="${i18n("midi-qol.buttons.attack")}">
        <dnd5e-icon src="systems/dnd5e/icons/svg/activity/attack.svg"></dnd5e-icon>
      </button>
    `);
  }
  if (activity.hasDamage) {
    buttons.push(`
      <button type="button" data-midi-action="damage" data-item-id="${itemId}" data-activity-id="${activityId}"
              data-tooltip="${i18n("midi-qol.buttons.damage")}">
        <dnd5e-icon src="systems/dnd5e/icons/svg/activity/damage.svg"></dnd5e-icon>
      </button>
    `);
    if (item.isVersatile) {
      buttons.push(`
        <button type="button" data-midi-action="versatile" data-item-id="${itemId}" data-activity-id="${activityId}"
                data-tooltip="${i18n("midi-qol.buttons.versatileDamage")}">
          <i class="fas fa-hand-fist"></i>
        </button>
      `);
    }
  }
  if (activity.roll?.formula) {
    buttons.push(`
      <button type="button" data-midi-action="use" data-item-id="${itemId}" data-activity-id="${activityId}"
              data-tooltip="${i18n("midi-qol.buttons.itemUse")}">
        <dnd5e-icon src="systems/dnd5e/icons/svg/activity/utility.svg"></dnd5e-icon>
      </button>
    `);
  }
  if (buttons.length === 0) return;
  const buttonsHtml = `<div class="midi-activity-buttons">${buttons.join("")}</div>`;
  activityRow.insertAdjacentHTML("beforeend", buttonsHtml);
}
__name(injectActivityButtons, "injectActivityButtons");
function attachMidiButtonHandlers(html, app) {
  html.querySelectorAll("[data-midi-action]:not([data-midi-handled])").forEach((button) => {
    button.setAttribute("data-midi-handled", "true");
    button.addEventListener("click", async (event) => {
      event.preventDefault();
      event.stopPropagation();
      const btn = event.currentTarget;
      const action = btn.dataset.midiAction;
      const itemId = btn.dataset.itemId;
      const activityId = btn.dataset.activityId;
      const actor = app.document ?? app.actor;
      const item = actor?.items?.get(itemId);
      if (!item) return;
      if (debugEnabled > 1) debug("midi sheet button handler", action, item.name, activityId);
      const activity = activityId ? item.system.activities?.get(activityId) : null;
      switch (action) {
        case "info":
          await item.displayCard();
          break;
        case "attack":
          if (activity?.rollAttack) {
            let configure = !isAutoFastForward2("attack");
            if (areMidiKeysPressed(event, "RollToggle")) configure = !configure;
            await activity.rollAttack({ event }, { configure }, { create: true });
          }
          break;
        case "damage":
          if (activity?.rollDamage) {
            let configure = !isAutoFastForward2("damage");
            if (areMidiKeysPressed(event, "RollToggle")) configure = !configure;
            await activity.rollDamage({ event, versatile: false }, { configure }, { create: true });
          }
          break;
        case "versatile":
          if (activity?.rollDamage) {
            let configure = !isAutoFastForward2("damage");
            if (areMidiKeysPressed(event, "RollToggle")) configure = !configure;
            await activity.rollDamage({ event, versatile: true }, { configure }, { create: true });
          }
          break;
        case "use":
          if (activity?.use) {
            await activity.use({ event }, {}, {});
          }
          break;
      }
    });
  });
}
__name(attachMidiButtonHandlers, "attachMidiButtonHandlers");

// src/module/RollStats.ts
var blankStat = {
  numAttacks: 0,
  numAttack20: 0,
  numAttackFumble: 0,
  numAttackCritical: 0,
  numAttackAdvantage: 0,
  numAttackDisadvantage: 0,
  criticalDamageTotal: 0,
  numAttackMisses: 0,
  attackRollsDiceTotal: 0,
  attackRollTotal: 0,
  numD20Rolls: 0,
  numDamageRolls: 0,
  damageApplied: 0,
  damageTotal: 0,
  numSaves: 0,
  numSaveSuccess: 0,
  numSaveFail: 0,
  maxDamage: 0,
  healingDone: 0,
  kills: 0,
  damageTaken: 0,
  healingReceived: 0,
  numConcentrationSaves: 0,
  numConcentrationSuccess: 0,
  numConcentrationFail: 0,
  numDeathSaves: 0,
  numDeathSaveSuccess: 0,
  numDeathSaveFail: 0
};
var blankStats = {
  session: foundry.utils.duplicate(blankStat),
  lifetime: foundry.utils.duplicate(blankStat),
  itemStats: {},
  sessionDamageTakenByType: {},
  lifetimeDamageTakenByType: {},
  sessionDamageDealtByType: {},
  lifetimeDamageDealtByType: {},
  sessionDamageAppliedByType: {},
  lifetimeDamageAppliedByType: {}
};
var RollStats = class {
  static {
    __name(this, "RollStats");
  }
  currentStats;
  showStats() {
    new RollStatsDisplay({ stats: this, playersOnly: configSettings.playerStatsOnly }).render({ force: true });
  }
  getEntityStats(id, collection) {
    if (!id || !collection) return;
    if (!this.currentStats[id]) {
      const entity = collection?.get(id);
      if (!entity) return;
      if (entity instanceof Actor && configSettings.playerStatsOnly && !entity.hasPlayerOwner) return;
      if (entity instanceof User && configSettings.playerStatsOnly && entity.isGM) return;
      this.currentStats[id] = foundry.utils.duplicate({ ...blankStats, name: collection?.get(id)?.name ?? "" });
    } else {
      this.currentStats[id] = foundry.utils.mergeObject(
        this.currentStats[id],
        blankStats,
        { overwrite: false, inplace: true, insertKeys: true, insertValues: true }
      );
    }
    for (const scope of [this.currentStats[id].session, this.currentStats[id].lifetime]) {
      for (const key of Object.keys(blankStat)) {
        if (!Number.isFinite(scope[key])) scope[key] = 0;
      }
    }
    return this.currentStats[id];
  }
  prepareStats() {
    const stats = foundry.utils.duplicate(this.currentStats);
    Object.keys(stats).forEach((aid) => {
      const actStats = stats[aid];
      const lifetime = actStats.lifetime;
      const session = actStats.session;
      lifetime.attackRollAverage = this.toPrecision(lifetime.attackRollTotal / (lifetime.numAttacks || 1), 1);
      session.attackRollAverage = this.toPrecision(session.attackRollTotal / (session.numAttacks || 1), 1);
      lifetime.damageTotalAverage = this.toPrecision(lifetime.damageTotal / (lifetime.numAttacks || 1), 1);
      session.damageTotalAverage = this.toPrecision(session.damageTotal / (session.numAttacks || 1), 1);
      lifetime.damageAppliedAverage = this.toPrecision(lifetime.damageApplied / (lifetime.numAttacks || 1), 1);
      session.damageAppliedAverage = this.toPrecision(session.damageApplied / (session.numAttacks || 1), 1);
      lifetime.hitRate = this.toPrecision(lifetime.numDamageRolls / (lifetime.numAttacks || 1) * 100, 0);
      session.hitRate = this.toPrecision(session.numDamageRolls / (session.numAttacks || 1) * 100, 0);
      lifetime.criticalDamageAverage = this.toPrecision(lifetime.criticalDamageTotal / (lifetime.numAttackCritical || 1), 1);
      session.criticalDamageAverage = this.toPrecision(session.criticalDamageTotal / (session.numAttackCritical || 1), 1);
      Object.keys(actStats.itemStats).forEach((iid) => {
        for (const itemStats of [actStats.itemStats[iid].session, actStats.itemStats[iid].lifetime]) {
          if (!itemStats) continue;
          itemStats.attackRollAverage = this.toPrecision(itemStats.attackRollTotal / (itemStats.numAttacks || 1), 1);
          itemStats.damageTotalAverage = this.toPrecision(itemStats.damageTotal / (itemStats.numAttacks || 1), 1);
          itemStats.damageAppliedAverage = this.toPrecision(itemStats.damageApplied / (itemStats.numAttacks || 1), 1);
          itemStats.criticalDamageAverage = this.toPrecision(itemStats.criticalDamageTotal / (itemStats.numAttackCritical || 1), 1);
        }
      });
    });
    return stats;
  }
  getItemStats(item, itemName, id, collection) {
    if (!itemName) return { name: "", session: foundry.utils.duplicate(blankStat), lifetime: foundry.utils.duplicate(blankStat) };
    let currentStats = this.getEntityStats(id, collection);
    if (!currentStats) return;
    if (!currentStats.itemStats[itemName]) {
      currentStats.itemStats[itemName] = { name: itemName, session: foundry.utils.duplicate(blankStat), lifetime: foundry.utils.duplicate(blankStat) };
    }
    if (!currentStats.itemStats[itemName].lifetime) {
      currentStats.itemStats[itemName].lifetime = foundry.utils.duplicate(blankStat);
    }
    for (const scope of [currentStats.itemStats[itemName].session, currentStats.itemStats[itemName].lifetime]) {
      for (const key of Object.keys(blankStat)) {
        if (!Number.isFinite(scope[key])) scope[key] = 0;
      }
    }
    return currentStats.itemStats[itemName];
  }
  rollCount;
  constructor() {
    this.currentStats = game.settings.get("midi-qol", "RollStats");
    this.rollCount = 0;
  }
  async endSession() {
    if (!game.user?.isGM) return;
    Object.keys(this.currentStats).forEach((actorId) => {
      this.currentStats[actorId].session = foundry.utils.duplicate(blankStat);
      Object.values(this.currentStats[actorId].itemStats).forEach((item) => {
        item.session = foundry.utils.duplicate(blankStat);
      });
      this.currentStats[actorId].sessionDamageTakenByType = {};
      this.currentStats[actorId].sessionDamageDealtByType = {};
      this.currentStats[actorId].sessionDamageAppliedByType = {};
    });
    await game.settings.set("midi-qol", "RollStats", this.currentStats);
  }
  async clearStats() {
    if (!game.user?.isGM) return;
    this.currentStats = {};
    await game.settings.set("midi-qol", "RollStats", {});
  }
  async clearActorStats(actorId) {
    timedExecuteAsGM("removeStatsForActorId", {
      actorId
    });
  }
  GMremoveActorStats(actorId) {
    if (!game.user?.isGM) return;
    delete this.currentStats[actorId];
    game.settings.set("midi-qol", "RollStats", this.currentStats);
  }
  toPrecision(number, digits) {
    if (!Number.isFinite(number)) return 0;
    return Math.round(number * 10 ** digits) / 10 ** digits;
  }
  get statData() {
    return this.prepareStats();
  }
  fetchStats() {
    this.currentStats = game.settings.get("midi-qol", "RollStats");
  }
  // ==================== Client-side methods ====================
  // These send raw roll data to the GM via socket. The GM applies
  // the increments to its authoritative copy (see GMrecordStats).
  addDamage(hpDamage, totalDamage, item, maxDamage = 0, healingDone = 0, kills = 0, critical = false) {
    const actorId = item?.actor?.id;
    if (!actorId) return;
    const hasAttackActivity = !!(item && item.system.activities?.some((a) => a.hasAttack));
    const userId = game.user && item.actor.testUserPermission(game.user, "OWNER") ? game.user.id : void 0;
    timedExecuteAsGM("recordStats", {
      type: "damage",
      actorId,
      userId,
      itemName: item?.name ?? "",
      hpDamage,
      totalDamage,
      maxDamage,
      healingDone,
      kills,
      hasAttackActivity,
      critical
    });
    Hooks.callAll("midi-qol.StatsUpdated");
  }
  addSaveResult(numSaves, numSuccess, numFail, item) {
    const actorId = item?.actor?.id;
    if (!actorId) return;
    const userId = game.user && item.actor.testUserPermission(game.user, "OWNER") ? game.user.id : void 0;
    timedExecuteAsGM("recordStats", {
      type: "saveResult",
      actorId,
      userId,
      itemName: item.name,
      numSaves,
      numSuccess,
      numFail
    });
    Hooks.callAll("midi-qol.StatsUpdated");
  }
  addAttackRoll({ rawRoll, fumble, critical, total, advantageMode }, item) {
    const actorId = item.actor?.id;
    if (!actorId) return;
    const userId = game.user && item.actor?.testUserPermission(game.user, "OWNER") ? game.user.id : void 0;
    timedExecuteAsGM("recordStats", {
      type: "attackRoll",
      actorId,
      userId,
      itemName: item.name,
      rawRoll,
      fumble,
      critical,
      total,
      advantageMode
    });
    Hooks.callAll("midi-qol.StatsUpdated");
  }
  addDamageTaken(actorId, hpDamage, damageByType) {
    if (!actorId || hpDamage === 0) return;
    timedExecuteAsGM("recordStats", {
      type: "damageTaken",
      actorId,
      hpDamage,
      damageByType
    });
    Hooks.callAll("midi-qol.StatsUpdated");
  }
  addDamageDealtByType(item, dealtByType, appliedByType) {
    const actorId = item?.actor?.id;
    if (!actorId) return;
    const userId = game.user && item.actor.testUserPermission(game.user, "OWNER") ? game.user.id : void 0;
    timedExecuteAsGM("recordStats", {
      type: "damageDealtByType",
      actorId,
      userId,
      dealtByType,
      appliedByType
    });
  }
  addConcentrationResult(actor, success) {
    const actorId = actor?.id;
    if (!actorId) return;
    timedExecuteAsGM("recordStats", {
      type: "concentrationResult",
      actorId,
      success
    });
    Hooks.callAll("midi-qol.StatsUpdated");
  }
  addDeathSaveResult(actor, successes, failures) {
    const actorId = actor?.id;
    if (!actorId) return;
    timedExecuteAsGM("recordStats", {
      type: "deathSaveResult",
      actorId,
      successes,
      failures
    });
    Hooks.callAll("midi-qol.StatsUpdated");
  }
  // ==================== GM-side recording ====================
  // Only runs on the GM. Applies raw roll data as increments to
  // the authoritative stats. Called via socket from any client.
  /**
   * GM-only: apply a stat update message as increments to the authoritative stats.
   * This is the single entry point for all stat recording, ensuring no race conditions.
   */
  async GMrecordStats(msg) {
    if (!game.user?.isGM) return;
    switch (msg.type) {
      case "attackRoll":
        this._recordAttackRoll(msg);
        break;
      case "damage":
        this._recordDamage(msg);
        break;
      case "saveResult":
        this._recordSaveResult(msg);
        break;
      case "damageTaken":
        this._recordDamageTaken(msg);
        break;
      case "damageDealtByType":
        this._recordDamageDealtByType(msg);
        break;
      case "concentrationResult":
        this._recordConcentrationResult(msg);
        break;
      case "deathSaveResult":
        this._recordDeathSaveResult(msg);
        break;
    }
    this.rollCount = (this.rollCount + 1) % Math.max(1, configSettings.saveStatsEvery);
    if (this.rollCount === 0) {
      await game.settings.set("midi-qol", "RollStats", this.currentStats);
    }
  }
  _getPlayerStats(actorId, userId) {
    if (!userId) return void 0;
    const user = game.users?.get(userId);
    if (!user) return void 0;
    const actor = game.actors?.get(actorId);
    if (!actor || !actor.testUserPermission(user, "OWNER")) return void 0;
    return this.getEntityStats(userId, game.users);
  }
  _recordAttackRoll(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    const playerStats = this._getPlayerStats(msg.actorId, msg.userId);
    for (const stats of [actorStats, playerStats]) {
      if (!stats) continue;
      const itemData = stats === actorStats ? this.getItemStats(void 0, msg.itemName, msg.actorId, game.actors) : this.getItemStats(void 0, msg.itemName, msg.userId ?? "", game.users);
      for (const s of [stats.session, stats.lifetime, itemData?.session, itemData?.lifetime]) {
        if (!s) continue;
        s.numAttacks += 1;
        if (msg.rawRoll === 20) s.numAttack20 += 1;
        if (msg.critical) s.numAttackCritical += 1;
        if (msg.fumble) s.numAttackFumble += 1;
        s.attackRollsDiceTotal += msg.rawRoll;
        s.attackRollTotal += msg.total;
        if (msg.advantageMode === 1) s.numAttackAdvantage += 1;
        else if (msg.advantageMode === -1) s.numAttackDisadvantage += 1;
      }
    }
  }
  _recordDamage(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    const playerStats = this._getPlayerStats(msg.actorId, msg.userId);
    const didDamage = msg.totalDamage > 0 || msg.hpDamage > 0;
    for (const stats of [actorStats, playerStats]) {
      if (!stats) continue;
      const itemData = stats === actorStats ? this.getItemStats(void 0, msg.itemName, msg.actorId, game.actors) : this.getItemStats(void 0, msg.itemName, msg.userId ?? "", game.users);
      for (const s of [stats.session, stats.lifetime, itemData?.session, itemData?.lifetime]) {
        if (!s) continue;
        const isItemStats = s === itemData?.session || s === itemData?.lifetime;
        if (didDamage) {
          s.numDamageRolls += 1;
          s.damageApplied += msg.hpDamage;
          s.damageTotal += msg.totalDamage;
          s.maxDamage = Math.max(s.maxDamage, msg.maxDamage);
          if (msg.critical) s.criticalDamageTotal += msg.totalDamage;
        } else if (msg.healingDone > 0 && isItemStats) {
          s.numDamageRolls += 1;
          s.damageApplied -= msg.healingDone;
          s.damageTotal -= msg.healingDone;
        }
        s.healingDone += msg.healingDone;
        s.kills += msg.kills;
        if ((didDamage || msg.healingDone > 0) && !msg.hasAttackActivity) {
          s.numAttacks += 1;
        }
      }
    }
  }
  _recordSaveResult(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    const playerStats = this._getPlayerStats(msg.actorId, msg.userId);
    for (const stats of [actorStats, playerStats]) {
      if (!stats) continue;
      const itemData = stats === actorStats ? this.getItemStats(void 0, msg.itemName, msg.actorId, game.actors) : this.getItemStats(void 0, msg.itemName, msg.userId ?? "", game.users);
      for (const s of [stats.session, stats.lifetime, itemData?.session, itemData?.lifetime]) {
        if (!s) continue;
        s.numSaves += msg.numSaves;
        s.numSaveSuccess += msg.numSuccess;
        s.numSaveFail += msg.numFail;
      }
    }
  }
  _recordDamageTaken(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    if (msg.hpDamage > 0) {
      actorStats.session.damageTaken += msg.hpDamage;
      actorStats.lifetime.damageTaken += msg.hpDamage;
      for (const [type, amount] of Object.entries(msg.damageByType)) {
        if (amount <= 0) continue;
        actorStats.sessionDamageTakenByType[type] = (actorStats.sessionDamageTakenByType[type] ?? 0) + amount;
        actorStats.lifetimeDamageTakenByType[type] = (actorStats.lifetimeDamageTakenByType[type] ?? 0) + amount;
      }
    } else {
      const healing = Math.abs(msg.hpDamage);
      actorStats.session.healingReceived += healing;
      actorStats.lifetime.healingReceived += healing;
    }
  }
  _recordDamageDealtByType(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    const playerStats = this._getPlayerStats(msg.actorId, msg.userId);
    for (const stats of [actorStats, playerStats]) {
      if (!stats) continue;
      for (const [type, amount] of Object.entries(msg.dealtByType)) {
        if (amount <= 0) continue;
        stats.sessionDamageDealtByType[type] = (stats.sessionDamageDealtByType[type] ?? 0) + amount;
        stats.lifetimeDamageDealtByType[type] = (stats.lifetimeDamageDealtByType[type] ?? 0) + amount;
      }
      for (const [type, amount] of Object.entries(msg.appliedByType)) {
        if (amount <= 0) continue;
        stats.sessionDamageAppliedByType[type] = (stats.sessionDamageAppliedByType[type] ?? 0) + amount;
        stats.lifetimeDamageAppliedByType[type] = (stats.lifetimeDamageAppliedByType[type] ?? 0) + amount;
      }
    }
  }
  _recordConcentrationResult(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    for (const s of [actorStats.session, actorStats.lifetime]) {
      s.numConcentrationSaves += 1;
      if (msg.success) s.numConcentrationSuccess += 1;
      else s.numConcentrationFail += 1;
    }
  }
  _recordDeathSaveResult(msg) {
    const actorStats = this.getEntityStats(msg.actorId, game.actors);
    if (!actorStats) return;
    for (const s of [actorStats.session, actorStats.lifetime]) {
      s.numDeathSaves += 1;
      s.numDeathSaveSuccess += msg.successes;
      s.numDeathSaveFail += msg.failures;
    }
  }
  // ==================== Legacy GM methods ====================
  exportToJSON() {
    const data = this.currentStats;
    const filename = `fvtt-midi-qol-stats.json`;
    foundry.utils.saveDataToFile(JSON.stringify(data, null, 2), "text/json", filename);
  }
  headerLine = `"Actor", "Item Name", "#Attacks", "# Nat20", "#Fumbles", "#Critical", "#Advantage", "#Disadvantage", "Critical Damage Total", "Attack Roll Dice Total", "Attack Roll Total", "Damage Rolls", "Total Damage Applied", "Damage Total", "#Saves", "#Save Success", "#Save Fail", "Max Damage", "Max Heal", "Kills", "Damage Taken", "Healing Received", "#Conc Saves", "#Conc Success", "#Conc Fail", "#Death Saves", "#Death Save Success", "#Death Save Fail"`;
  dumpStatLine(actorName, itemName, stats) {
    return `"${actorName}","${itemName}", ${stats.numAttacks || 0}, ${stats.numAttack20 || 0}, ${stats.numAttackFumble || 0}, ${stats.numAttackCritical || 0}, ${stats.numAttackAdvantage || 0}, ${stats.numAttackDisadvantage || 0}, ${stats.criticalDamageTotal || 0}, ${stats.attackRollsDiceTotal || 0}, ${stats.attackRollTotal || 0}, ${stats.numDamageRolls || 0}, ${stats.damageApplied || 0}, ${stats.damageTotal || 0}, ${stats.numSaves || 0}, ${stats.numSaveSuccess || 0}, ${stats.numSaveFail || 0}, ${stats.maxDamage || 0}, ${stats.healingDone || 0}, ${stats.kills || 0}, ${stats.damageTaken || 0}, ${stats.healingReceived || 0}, ${stats.numConcentrationSaves || 0}, ${stats.numConcentrationSuccess || 0}, ${stats.numConcentrationFail || 0}, ${stats.numDeathSaves || 0}, ${stats.numDeathSaveSuccess || 0}, ${stats.numDeathSaveFail || 0}`;
  }
  exportToCSV() {
    let csvText = foundry.utils.duplicate(this.headerLine) + "\n";
    for (const period of ["session", "lifetime"]) {
      for (let actorStats of Object.values(this.currentStats)) {
        csvText += this.dumpStatLine(actorStats.name, period, actorStats[period]) + "\n";
        for (let itemStat of Object.values(actorStats.itemStats)) {
          csvText += this.dumpStatLine(actorStats.name, `${itemStat.name} (${period})`, itemStat[period]) + "\n";
        }
      }
    }
    const filename = `fvtt-midi-qol-stats.csv`;
    foundry.utils.saveDataToFile(csvText, "text/csv", filename);
  }
  async saveStats() {
    if (game.user?.isGM) {
      await game.settings.set("midi-qol", "RollStats", this.currentStats);
    } else {
      await timedExecuteAsGM("saveAllStats", {});
    }
  }
  async GMsaveAllStats() {
    if (!game.user?.isGM) return;
    await game.settings.set("midi-qol", "RollStats", this.currentStats);
  }
};

// src/module/MidiKeyManager.ts
var MidiKeyManager = class {
  static {
    __name(this, "MidiKeyManager");
  }
  constructor() {
  }
  initKeyMappings() {
    if (!game.keybindings) {
      error2("MidiKeyManager | initKeyMappings | Game is not initialized");
      return;
    }
    const keybindings = game.keybindings;
    const normalPrecedence = CONST.KEYBINDING_PRECEDENCE.NORMAL;
    keybindings.register("midi-qol", "NoOptionalRules", {
      name: "midi-qol.NoOptionalRules.Name",
      hint: "midi-qol.NoOptionalRules.Hint",
      editable: [],
      restricted: true,
      // Restrict this Keybinding to gamemaster only?
      precedence: normalPrecedence
    });
    keybindings.register("midi-qol", "Versatile", {
      name: i18n("DND5E.Versatile") ?? "Versatile",
      hint: "midi-qol.KeysVersatile.Hint",
      editable: [
        { key: "KeyV" }
      ],
      restricted: false,
      // Restrict this Keybinding to gamemaster only?
      precedence: normalPrecedence
    });
    keybindings.register("midi-qol", "RollToggle", {
      name: i18n("midi-qol.RollToggle.Name") ?? "Roll Toggle",
      hint: i18n("midi-qol.RollToggle.Hint"),
      editable: [
        { key: "KeyF" }
      ],
      restricted: false,
      // Restrict this Keybinding to gamemaster only?
      precedence: normalPrecedence
    });
  }
};

// src/module/apps/UndoWorkflow.ts
var { ApplicationV2: ApplicationV212, HandlebarsApplicationMixin: HandlebarsApplicationMixin13 } = foundry.applications.api;
var UndoWorkflow = class extends HandlebarsApplicationMixin13(ApplicationV212) {
  static {
    __name(this, "UndoWorkflow");
  }
  undoAddedHookId;
  undoRemovedHookId;
  constructor(options) {
    super(options);
    this.undoAddedHookId = Hooks.on("midi-qol.addUndoEntry", () => {
      this.render();
    });
    this.undoRemovedHookId = Hooks.on("midi-qol.removeUndoEntry", () => {
      this.render();
    });
    if (!configSettings.undoWorkflow) {
      configSettings.undoWorkflow = true;
      game.settings.set("midi-qol", "ConfigSettings", configSettings);
      ui.notifications?.warn("Undo Workflow enabled");
    }
  }
  static PARTS = {
    main: { template: "modules/midi-qol/templates/apps/UndoWorkflow/undo-workflow.hbs" }
  };
  static DEFAULT_OPTIONS = {
    id: "midi-qol-undo-workflow",
    window: {
      title: "midi-qol.UndoWorkflow.title",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      width: 400,
      height: 700
    }
  };
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.entries = [];
    context.queueSize = new TextEncoder().encode(JSON.stringify(undoDataQueue)).length.toLocaleString();
    context.queueCount = undoDataQueue.length;
    for (let undoEntry of undoDataQueue) {
      const entry = {};
      entry.actorName = undoEntry.actorName;
      entry.itemName = undoEntry.itemName;
      entry.userName = undoEntry.userName;
      entry.targets = [];
      for (let targetEntry of undoEntry.allTargets ?? []) {
        entry.targets.push(targetEntry);
      }
      context.entries.push(entry);
    }
    return context;
  }
  async _onRender(context, options) {
    super._onRender(context, options);
    this.element.querySelector("#undo-first-workflow")?.addEventListener("click", (e) => {
      undoMostRecentWorkflow();
    });
    this.element.querySelector("#remove-first-workflow")?.addEventListener("click", (e) => {
      removeMostRecentWorkflow();
    });
  }
  get title() {
    return i18n("midi-qol.UndoWorkflow.title") ?? "Undo Workflow";
  }
  async close(options = {}) {
    Hooks.off("midi-qol.addUndoEntry", this.undoAddedHookId);
    Hooks.off("midi-qol.removeUndoEntry", this.undoRemovedHookId);
    return super.close(options);
  }
};
function showUndoWorkflowApp() {
  if (game.user?.isGM) {
    new UndoWorkflow({}).render({ force: true });
  } else {
    ui.notifications?.warn("midi-qol.UndoWorkflow.GMOnly");
  }
}
__name(showUndoWorkflowApp, "showUndoWorkflowApp");

// src/module/ChatClasses/ChatLogMidi.ts
var ChatLogMidi = class extends globalThis.dnd5e.applications.ChatLog5e {
  static {
    __name(this, "ChatLogMidi");
  }
  #lastId;
  #renderingBatch = false;
  #renderingQueue = new foundry.utils.Semaphore(1);
  #overflowingDebounce = foundry.utils.debounce(this.#setOverflowing.bind(this), 100);
  #pruneDebounce = foundry.utils.debounce(this.#doPrune.bind(this), 100);
  #renderedIds = /* @__PURE__ */ new Set();
  // Track rendered messages per-instance
  #doPrune() {
    const scroll = this.element?.querySelector(".chat-scroll .chat-log");
    const count = scroll?.children.length ?? 0;
    const maxSize = CONFIG.ChatMessage.batchSize ?? 25;
    if (count >= maxSize * 2) {
      for (let index = 0; index < count - maxSize; index++) {
        const firstChild = scroll?.firstElementChild;
        const messageId = firstChild?.dataset?.messageId;
        if (messageId) this.#renderedIds.delete(messageId);
        if (!this.isPopout && messageId) {
          const message = game.messages?.get(messageId);
          if (message) message.logged = false;
        }
        firstChild?.remove();
        this.#lastId = messageId;
      }
    }
  }
  async pruneElement() {
    this.#pruneDebounce();
  }
  async scrollBottom({ popout = false, waitImages = false, scrollOptions = {} } = {}) {
    if (!this.rendered) return;
    this.pruneElement();
    return super.scrollBottom({ popout, waitImages, scrollOptions });
  }
  /** @inheritDoc */
  async close(options = {}) {
    this.#renderedIds.clear();
    this.#lastId = void 0;
    return super.close(options);
  }
  async #doRenderBatch(size) {
    if (!this.rendered) {
      this.#renderingBatch = false;
      return;
    }
    const messages = game.messages?.contents;
    if (!messages) return;
    const log5 = this.element?.querySelector(".chat-log");
    if (!log5) return;
    let lastIdx = messages.findIndex((m) => m.id === this.#lastId);
    lastIdx = lastIdx > -1 ? lastIdx : messages.length;
    if (!lastIdx) {
      this.#renderingBatch = false;
      return;
    }
    const targetIdx = Math.max(lastIdx - size, 0);
    const elements = [];
    for (let i = targetIdx; i < lastIdx; i++) {
      const message = messages[i];
      if (!message.visible) continue;
      if (this.#renderedIds.has(message.id)) continue;
      this.#renderedIds.add(message.id);
      if (!this.isPopout) message.logged = true;
      try {
        elements.push(await this.constructor.renderMessage(message));
      } catch (err) {
        Hooks.onError("ChatLog#doRenderBatch", err, {
          msg: `Chat message ${message.id} failed to render`,
          log: "error"
        });
      }
    }
    log5.prepend(...elements);
    this.#lastId = messages[targetIdx].id;
    this.#renderingBatch = false;
    if (!this.isPopout) this.#overflowingDebounce();
  }
  #setOverflowing() {
    const scroll = this.element?.querySelector(".chat-scroll");
    scroll?.classList.toggle("overflowed", scroll.scrollHeight > scroll.offsetHeight);
  }
  async renderBatch(size) {
    if (this.#renderingBatch) return;
    this.#renderingBatch = true;
    return this.#renderingQueue.add(this.#doRenderBatch.bind(this), size);
  }
};

// src/module/lib/midiCompletions.ts
var MIDI_COMPLETIONS = {
  MidiQOL: {
    children: {
      // Workflow classes
      Workflow: {
        type: "class",
        detail: "class",
        info: "Main workflow class",
        children: {
          getWorkflow: { type: "function", detail: "(uuid) => Workflow", info: "Get a workflow by its message UUID" }
        }
      },
      DamageOnlyWorkflow: { type: "class", detail: "class", info: "Workflow for damage-only rolls" },
      TrapWorkflow: { type: "class", detail: "class", info: "Workflow for trap effects" },
      RollModifierTracker: { type: "class", detail: "class", info: "Tracks roll modifiers (advantage, critical, etc.)" },
      // Item/activity use
      completeItemUse: { type: "function", detail: "(item, config?, dialog?, message?) => Promise<Workflow>", info: "Complete an item use workflow.\n- item: Item or UUID string\n- config.chooseActivity: show dialog to pick activity\n- config.midiOptions: {advantage, disadvantage, critical, fastForward, targetUuids, ...}\nReturns: Workflow or undefined" },
      completeActivityUse: { type: "function", detail: "(activity, usage?, dialog?, message?) => Promise<Workflow>", info: "Complete an activity use workflow.\n- activity: Activity or UUID string\n- usage.midiOptions: {advantage, disadvantage, critical, fastForward, fastForwardAttack, fastForwardDamage, targetUuids, targetsToUse, rollAs, ignoreUserTargets, ...}\nReturns: Workflow or undefined" },
      // Distance and targeting
      computeDistance: { type: "function", detail: "(token1, token2, options?) => number", info: "Compute distance between two tokens in grid units.\n- options.wallsBlock: block if no LOS (default false)\n- options.includeCover: include cover calc (default true)\n- options.includeElevation: include height difference\nReturns: distance or -1 if fully blocked" },
      checkDistance: { type: "function", detail: "(t1, t2, distance, options?) => boolean", info: "Check if two tokens are within a given distance.\n- distance: max distance in grid units\n- options.wallsBlock: block if no LOS (default false)\n- options.includeCover: include cover calc (default true)\nReturns: true if distance <= threshold" },
      findNearby: { type: "function", detail: "(disposition, token, distance, options?) => Token[]", info: 'Find all tokens within distance matching disposition.\n- disposition: -1=hostile, 0=neutral, 1=friendly, null/"all"=any, or array\n- options: {includeIncapacitated, canSee, isSeen, includeToken, maxSize, relative}\n- relative (default true): disposition relative to token\nReturns: Token[]' },
      findNearbyCount: { type: "function", detail: "(disposition, token, distance, options?) => number", info: 'Count tokens within distance matching disposition.\n- disposition: -1=hostile, 0=neutral, 1=friendly, null/"all"=any, or array\n- options: {includeIncapacitated, canSee, isSeen, includeToken, maxSize, relative}\nReturns: number of matching tokens' },
      checkNearby: { type: "function", detail: "(disposition, token, distance, options?) => boolean", info: 'Check if any token is within distance matching disposition.\n- disposition: -1=hostile, 0=neutral, 1=friendly, null/"all"=any\n- options: {includeIncapacitated, canSee, isSeen, includeToken, maxSize, relative}\nReturns: true if at least one match found' },
      computeCoverBonus: { type: "function", detail: "(attacker, target, activity?) => number", info: "Compute cover AC bonus for target against attacker.\n- attacker/target: Token or TokenDocument\n- activity: optional, used for ignore-cover checks\nReturns: 0=none, 2=half, 5=three-quarters, 999=full" },
      checkActivityRange: { type: "function", detail: "(activity, token, targets?, showWarning?) => object", info: `Check if targets are within activity's range.
- showWarning: display UI warning if out of range (default true)
Returns: {result, attackingToken, range, longRange}
- result: "normal", "longRange", or "outOfRange"` },
      selectTargetsFromTemplates: { type: "function", detail: "(templates, selfTokenRef?, ignoreSelf?, AoETargetType?, autoTarget?) => Token[]", info: 'Target tokens in template area and update game.user.targets.\n- AoETargetType: "any", "enemy", "ally", "self", "neutral"\n- autoTarget: "none", "wallsBlock", "wallsBlockIgnoreDefeated", "wallsBlockIgnoreIncapacitated", "walledtemplates"\nReturns: Token[]' },
      computeTargetsFromTemplates: { type: "function", detail: "(templates, selfTokenRef?, ignoreSelf?, AoETargetType?, autoTarget?) => Token[]", info: 'Compute tokens in template area WITHOUT modifying game.user.targets.\n- AoETargetType: "any", "enemy", "ally", "self", "neutral"\n- autoTarget: "none", "wallsBlock", "wallsBlockIgnoreDefeated", "wallsBlockIgnoreIncapacitated"\nReturns: Token[]' },
      // Conditions and effects
      hasCondition: { type: "function", detail: "(actorRef, condition) => 0 | 1", info: 'Check if actor/token has a condition.\n- actorRef: Actor, Token, TokenDocument, or UUID string\n- condition: status id (e.g. "prone", "blinded", "concentrating", "stunned")\nReturns: 1 if condition exists, 0 if not or immune' },
      checkIncapacitated: { type: "function", detail: "(actorRef?, log?, warn?) => string | false", info: 'Check if actor is incapacitated.\n- actorRef: Actor, Token, or UUID string\n- log: log result to console (default true)\n- warn: show UI warning (default false)\nReturns: condition name (e.g. "dead", "unconscious") or false' },
      createConditionData: { type: "function", detail: "({actor, condition, ...}) => object", info: "Create condition/rollData for evalCondition.\n- Fields: {workflow, target, actor, item, activity, token, extraData, options}\nReturns: object with rollData, damage info, combat state for condition evaluation" },
      getConcentrationEffect: { type: "function", detail: "(actor?, item?) => ActiveEffect | undefined", info: "Get the concentration effect for an actor" },
      addConcentrationDependent: { type: "function", detail: "(actorRef, dependent, item?) => Promise", info: "Add a concentration dependent" },
      createEffects: { type: "function", detail: "({actorUuid, effects, options}) => Promise<boolean>", info: "Create active effects on an actor (runs as GM).\n- actorUuid: actor UUID string\n- effects: array of ActiveEffect.CreateData objects\n- options: {keepId: boolean}\nReturns: true on success, false on failure" },
      removeEffects: { type: "function", detail: "({actorUuid, effects}) => Promise<boolean>", info: "Remove active effects from an actor (runs as GM).\n- actorUuid: actor UUID string\n- effects: array of effect ID strings\nReturns: true on success, false on failure" },
      updateEffects: { type: "function", detail: "({actorUuid, updates}) => Promise", info: "Update effects on an actor by UUID" },
      chooseEffect: { type: "function", detail: "({actor, token?, item?, ...}) => Promise<ActiveEffect | false>", info: "Show dialog to choose which effect to apply from an item.\n- Fields: {actor, token, item, workflow, options}\n- options.chooseAll: auto-apply all without dialog\n- options.timeout: dialog timeout in ms\nReturns: selected ActiveEffect or false" },
      // Damage
      applyTokenDamage: { type: "function", detail: "(damageDetail, totalDamage, targets, item, saves, options?) => Promise<string[]>", info: "Apply damage to a set of tokens, creating damage cards.\n- damageDetail: [{value, type, properties}] array\n- targets/saves: Set<Token>\n- options: {workflow, superSavers, semiSuperSavers, forceApply, noConcentrationCheck}\nReturns: array of damage card UUIDs" },
      applyTokenDamageMany: { type: "function", detail: "(targetDamages, item, options?) => Promise<string[]>", info: "Apply different damage/healing amounts to multiple targets, producing a single damage card.\n- targetDamages: [{token, damageDetail, totalDamage}] \u2014 each entry has its own damage detail and total\n- item: Item responsible for the damage (used for card display)\n- options: {workflow, forceApply, updateOptions}\nReturns: array of damage card UUIDs" },
      createDamageDetail: { type: "function", detail: "({roll, activity, defaultType?}) => DamageDescription[]", info: "Create damage detail from roll results.\n- roll: Roll or Roll[]\n- activity: Activity (for damage type info)\n- defaultType: fallback damage type\nReturns: [{value, type, formula, properties}]" },
      getTraitMult: { type: "function", detail: "(actor, damageType, item, props?) => number", info: 'Get damage trait multiplier for an actor.\n- damageType: e.g. "fire", "slashing", "healing"\n- props: string[] of damage properties (e.g. "mgc")\nReturns: -1=healing, 0=immune, 0.5=resistant, 1=normal, 2=vulnerable' },
      modifyDamageBy: { type: "function", detail: "({damageItem, value, multiplier?, type?, reason?}) => object", info: "Modify damage during isDamaged macro pass only.\n- damageItem: DamageListEntry from workflow\n- value: amount to add/subtract\n- multiplier: optional damage multiplier\n- type/reason: for logging\nReturns: damage modification object" },
      // Rolls
      addRollTo: { type: "function", detail: "(roll, bonusRoll) => Roll", info: "Add a bonus roll to another roll" },
      doConcentrationCheck: { type: "function", detail: "(actor, saveDC) => Promise<Roll>", info: "Perform a concentration check" },
      contestedRoll: { type: "function", detail: "(data) => Promise<{result, rolls}>", info: 'Perform a contested roll between two actors.\n- data.source/target: {rollType, ability, token, rollOptions}\n- rollType: "test" (ability check), "save", "skill"\n- data.success/failure/drawn: callback functions\nReturns: {result: number (+source wins, -target wins, 0=draw), rolls}' },
      displayDSNForRoll: { type: "function", detail: "(rolls?, rollType?, rollMode?) => Promise", info: "Display Dice So Nice 3D dice animation" },
      sumRolls: { type: "function", detail: "(rolls?, countHealing?) => number", info: "Sum roll results" },
      computeAbilityAdvantage: { type: "function", detail: "(actor, ability) => string | null", info: "Compute advantage/disadvantage for ability roll" },
      computeSkillAdvantage: { type: "function", detail: "(actor, skill) => string | null", info: "Compute advantage/disadvantage for skill roll" },
      computeToolAdvantage: { type: "function", detail: "(actor, tool) => string | null", info: "Compute advantage/disadvantage for tool check" },
      // Token/actor helpers
      tokenForActor: { type: "function", detail: "(actor) => Token | undefined", info: "Get the first token for an actor" },
      actorFromUuid: { type: "function", detail: "(uuid?) => Actor | null", info: "Get actor from UUID string" },
      playerFor: { type: "function", detail: "(target?) => User | null", info: "Get the player controlling a token" },
      playerForActor: { type: "function", detail: "(actor?) => User | undefined", info: "Get the player controlling an actor" },
      getTokenPlayerName: { type: "function", detail: "(token, checkGM?) => string", info: "Get the player name controlling a token" },
      raceOrType: { type: "function", detail: "(entity) => string", info: "Get race (PCs) or creature type (NPCs)" },
      sizeNumeric: { type: "function", detail: "(size: string) => number | undefined", info: 'Get numerical size index (0=tiny, 1=sm, 2=med, 3=lg, 4=huge, 5=grg). Accepts short keys ("sm", "lg") or long names ("Small", "Large"), case insensitive.' },
      typeOrRace: { type: "function", detail: "(entity) => string", info: "Get creature type (NPCs) or race (PCs)" },
      canSee: { type: "function", detail: "(token, target) => boolean", info: "Check if token can see target (sight-based detection only).\n- token/target: Token, TokenDocument, or UUID string\nReturns: true if token has line of sight to target" },
      canSense: { type: "function", detail: "(token, target, validModes?) => boolean", info: 'Check if token can detect target via detection modes.\n- validModes: ["all"] (default), or specific modes:\n  "basicSight", "lightPerception", "blindsight", "darkvision", "tremorsense", "truesight"\nReturns: true if any valid mode detects target' },
      canSenseModes: { type: "function", detail: "(token, target, validModes?) => string[]", info: "Get detection modes token can use to sense target" },
      getOrCreateTokenForActor: { type: "function", detail: "(actor?) => Token | undefined", info: "Get or create a token for an actor" },
      moveToken: { type: "function", detail: "(tokenRef, newCenter, options?) => Promise", info: "Move a token to a new position (runs as GM). Uses Foundry's TokenDocument.move() for proper hook/region support.\n- tokenRef: Token, TokenDocument, or UUID string\n- newCenter: {x, y, elevation?} canvas center coordinates; elevation in grid units (default: unchanged)\n- options: {animate?, teleport?, ignoreWalls?, ignoreTokens?, autoRotate?}\n  animate: animate the movement (default true); false = instant but still checks walls/tokens\n  teleport: instant move, ignores walls and tokens (default false)\n  ignoreWalls: ignore wall collisions (default false)\n  ignoreTokens: ignore token blocking (default false)\n  autoRotate: rotate token to face movement direction (default false)" },
      moveTokenAwayFromPoint: { type: "function", detail: "(targetRef, distance, point, options?) => Promise", info: "Push a token away from a point (runs as GM). Uses Foundry's TokenDocument.move() for proper hook/region support.\n- targetRef: Token, TokenDocument, or UUID string\n- distance: distance to push in grid units\n- point: {x, y} origin point to push away from\n- options: {animate?, teleport?, ignoreWalls?, ignoreTokens?, autoRotate?}\n  animate: animate the movement (default true)\n  teleport: instant move, ignores walls and tokens (default false)\n  ignoreWalls: ignore wall collisions (default false)\n  ignoreTokens: ignore token blocking (default false)\n  autoRotate: rotate token to face movement direction (default false)" },
      teleportToken: { type: "function", detail: "(tokenRef, destination) => Promise", info: "Teleport a token instantly (runs as GM). Supports cross-scene teleportation.\n- tokenRef: Token, TokenDocument, or UUID string\n- destination: {x, y, elevation?, sceneUuid?}\n  x, y: center coordinates on destination scene\n  elevation: elevation in grid units (optional)\n  sceneUuid: destination scene UUID (omit for same scene)" },
      // Action economy
      hasUsedReaction: { type: "function", detail: "(actor) => boolean", info: "Check if reaction has been used this turn" },
      hasUsedBonusAction: { type: "function", detail: "(actor) => boolean", info: "Check if bonus action has been used this turn" },
      setReactionUsed: { type: "function", detail: "(actor, active?) => Promise", info: "Mark reaction as used (or remove if false)" },
      setBonusActionUsed: { type: "function", detail: "(actor) => Promise", info: "Mark bonus action as used" },
      removeReactionUsed: { type: "function", detail: "(actor, force?) => Promise", info: "Remove reaction used flag" },
      removeBonusActionUsed: { type: "function", detail: "(actor, force?) => Promise", info: "Remove bonus action used flag" },
      removeActionUsed: { type: "function", detail: "(actor) => Promise", info: "Remove action used flag" },
      // Over-time effects
      doOverTimeEffect: { type: "function", detail: "(actor, effect, startTurn?, options?) => Promise", info: "Execute an over-time effect" },
      // Conditions helpers
      removeHiddenCondition: { type: "function", detail: "(tokenRef?) => Promise", info: "Remove hidden condition from token" },
      removeInvisibleCondition: { type: "function", detail: "(tokenRef?) => Promise", info: "Remove invisible condition from token" },
      // Undo
      undoMostRecentWorkflow: { type: "function", detail: "() => Promise", info: "Undo the most recent workflow" },
      showUndoQueue: { type: "function", detail: "()", info: "Display the undo queue" },
      getUndoQueue: { type: "function", detail: "() => ChatMessage[]", info: "Get the undo queue" },
      // Macro resolution
      resolveItemMacro: { type: "function", detail: "(item, macroName, macroText, rollData) => Promise", info: "Resolve and execute an item macro" },
      resolveActivityMacro: { type: "function", detail: "(activity, macroName, macroText, rollData) => Promise", info: "Resolve and execute an activity macro" },
      // Condition evaluation
      evalCondition: { type: "function", detail: "(condition, conditionData, options?) => any", info: "Evaluate a condition expression using roll data.\n- condition: formula string with @references, number, or boolean\n- conditionData: rollData object (from createConditionData)\n- options: {errorReturn (default false), async (default false)}\nReturns: evaluated result (boolean/number), or Promise if async" },
      evalAllConditions: { type: "function", detail: "(actorRef, flag, conditionData) => Promise<boolean>", info: "Evaluate all conditions for a flag" },
      // Settings and config
      configSettings: { type: "function", detail: "() => object", info: "Get current midi-qol configuration settings" },
      checkRule: { type: "function", detail: "(rule) => any", info: "Get the value of a configured rule" },
      safeGetGameSetting: { type: "function", detail: "(scope, key) => any", info: "Safely get a game setting" },
      enableWorkflow: { type: "function", detail: "(workflowType) => boolean", info: "Check if a workflow type is enabled" },
      // Vitality system
      hasVitalitySystem: { type: "function", detail: "(actor?) => boolean", info: "Check if actor uses vitality system" },
      getVitalityItem: { type: "function", detail: "(actor?) => Item | undefined", info: "Get the vitality item for an actor" },
      getVitalityValue: { type: "function", detail: "(actor?) => number | undefined", info: "Get current vitality value" },
      getVitalityMax: { type: "function", detail: "(actor?) => number | undefined", info: "Get max vitality value" },
      // Origin / item resolution
      getItemFromEffectOrigin: { type: "function", detail: "(origin: string) => Item | undefined", info: "Resolve an effect's origin to the source Item, traversing concentration and other ActiveEffect chains. Handles recursive origins with cycle detection." },
      // Misc
      showItemInfo: { type: "function", detail: "(item) => Promise<ChatMessage>", info: "Show item information in chat" },
      getChanges: { type: "function", detail: "(actorOrItem, key) => ActiveEffect[]", info: "Get effects that modify a specific key" },
      addDependent: { type: "function", detail: "(document, dependent) => void", info: "Add a dependent to a document" },
      // Localization
      i18n: { type: "function", detail: "(key) => string", info: "Localize a string key" },
      i18nFormat: { type: "function", detail: "(key, data?) => string", info: "Localize and format a string key" },
      // Logging
      debug: { type: "function", detail: "(...args) => void", info: "Log debug message (only when debug enabled)" },
      log: { type: "function", detail: "(...args) => void", info: "Log normal message" },
      warn: { type: "function", detail: "(...args) => void", info: "Log warning message" },
      error: { type: "function", detail: "(...args) => void", info: "Log error message" }
    }
  },
  workflow: {
    type: "variable",
    detail: "Workflow",
    info: "The midi-qol workflow instance",
    children: {
      tracker: {
        type: "property",
        detail: "RollModifierTracker",
        info: "The attack roll modifier tracker",
        children: {
          advantage: {
            type: "property",
            detail: "AdvantageDisadvantageTracker",
            info: "Advantage sub-tracker",
            children: {
              add: { type: "method", detail: "(source?, displayName?)", info: "Add a source of advantage" },
              clear: { type: "method", detail: "()", info: "Clear active state" },
              suppress: { type: "method", detail: "(source?, displayName?)", info: "Suppress advantage" },
              setOverride: { type: "method", detail: "(source?, displayName?)", info: "Force advantage, bypassing suppression" },
              clearOverride: { type: "method", detail: "()", info: "Clear override state" },
              reset: { type: "method", detail: "()", info: "Reset all state" },
              isActive: { type: "property", detail: "true | undefined", info: "Computed final state" },
              isAdded: { type: "property", detail: "boolean", info: "Whether at least one source was added" },
              isSuppressed: { type: "property", detail: "boolean", info: "Whether suppressed" },
              hasOverride: { type: "property", detail: "boolean", info: "Whether override is set" },
              get: { type: "property", detail: "{ active, suppressed, override }", info: "Full state snapshot" }
            }
          },
          disadvantage: {
            type: "property",
            detail: "AdvantageDisadvantageTracker",
            info: "Disadvantage sub-tracker",
            children: {
              add: { type: "method", detail: "(source?, displayName?)", info: "Add a source of disadvantage" },
              clear: { type: "method", detail: "()", info: "Clear active state" },
              suppress: { type: "method", detail: "(source?, displayName?)", info: "Suppress disadvantage" },
              setOverride: { type: "method", detail: "(source?, displayName?)", info: "Force disadvantage, bypassing suppression" },
              clearOverride: { type: "method", detail: "()", info: "Clear override state" },
              reset: { type: "method", detail: "()", info: "Reset all state" },
              isActive: { type: "property", detail: "true | undefined", info: "Computed final state" },
              isAdded: { type: "property", detail: "boolean", info: "Whether at least one source was added" },
              isSuppressed: { type: "property", detail: "boolean", info: "Whether suppressed" },
              hasOverride: { type: "property", detail: "boolean", info: "Whether override is set" },
              get: { type: "property", detail: "{ active, suppressed, override }", info: "Full state snapshot" }
            }
          },
          critical: {
            type: "property",
            detail: "CriticalFumbleTracker",
            info: "Critical hit sub-tracker",
            children: {
              setNatural: { type: "method", detail: "(source, displayName?)", info: "Set from dice roll" },
              force: { type: "method", detail: "(source, displayName?)", info: "Force critical hit" },
              suppress: { type: "method", detail: "(source, displayName?)", info: "Prevent critical hit" },
              setOverride: { type: "method", detail: "(value, source, displayName?)", info: "Absolute control (true=force, false=prevent)" },
              clearOverride: { type: "method", detail: "()", info: "Clear override" },
              reset: { type: "method", detail: "()", info: "Reset all state" },
              isActive: { type: "property", detail: "boolean", info: "Computed final critical state" },
              isNatural: { type: "property", detail: "boolean", info: "From dice roll" },
              isForced: { type: "property", detail: "boolean", info: "Forced by flag" },
              isSuppressed: { type: "property", detail: "boolean", info: "Prevented by flag" },
              hasOverride: { type: "property", detail: "boolean", info: "Override is set" }
            }
          },
          fumble: {
            type: "property",
            detail: "CriticalFumbleTracker",
            info: "Fumble sub-tracker",
            children: {
              setNatural: { type: "method", detail: "(source, displayName?)", info: "Set from dice roll" },
              force: { type: "method", detail: "(source, displayName?)", info: "Force fumble" },
              suppress: { type: "method", detail: "(source, displayName?)", info: "Prevent fumble" },
              setOverride: { type: "method", detail: "(value, source, displayName?)", info: "Absolute control" },
              clearOverride: { type: "method", detail: "()", info: "Clear override" },
              reset: { type: "method", detail: "()", info: "Reset all state" },
              isActive: { type: "property", detail: "boolean", info: "Computed final fumble state" },
              isNatural: { type: "property", detail: "boolean", info: "From dice roll" },
              isForced: { type: "property", detail: "boolean", info: "Forced by flag" },
              isSuppressed: { type: "property", detail: "boolean", info: "Prevented by flag" },
              hasOverride: { type: "property", detail: "boolean", info: "Override is set" }
            }
          },
          modifiers: {
            type: "property",
            detail: "RollValueTracker",
            info: "Roll value modifier sub-tracker",
            children: {
              fail: { type: "method", detail: "(source, displayName?)", info: "Set auto-fail" },
              succeed: { type: "method", detail: "(source, displayName?)", info: "Set auto-success" },
              min: { type: "method", detail: "(value, source, displayName?)", info: "Set minimum roll value" },
              max: { type: "method", detail: "(value, source, displayName?)", info: "Set maximum roll value" },
              forceMin: { type: "method", detail: "(value, source, displayName?)", info: "Force minimum roll value" },
              forceMax: { type: "method", detail: "(value, source, displayName?)", info: "Force maximum roll value" },
              reset: { type: "method", detail: "()", info: "Reset all state" },
              willFail: { type: "property", detail: "boolean", info: "Auto-fail is set" },
              willSucceed: { type: "property", detail: "boolean", info: "Auto-success is set" },
              minValue: { type: "property", detail: "number", info: "Minimum roll value" },
              maxValue: { type: "property", detail: "number", info: "Maximum roll value" }
            }
          },
          hasAdvantage: { type: "property", detail: "true | undefined", info: "Effective advantage state" },
          hasDisadvantage: { type: "property", detail: "true | undefined", info: "Effective disadvantage state" },
          isCritical: { type: "property", detail: "boolean", info: "Computed final critical state" },
          isFumble: { type: "property", detail: "boolean", info: "Computed final fumble state" },
          advantageMode: { type: "property", detail: "number", info: "ADV_MODE.ADVANTAGE, NORMAL, or DISADVANTAGE" },
          attribution: { type: "property", detail: "object", info: "Attribution map { ADV: {...}, DIS: {...} }" },
          reset: { type: "method", detail: "(options?)", info: "Reset all sub-trackers and attribution" }
        }
      },
      targets: {
        type: "property",
        detail: "Set<Token5e>",
        info: "The targeted tokens",
        children: {
          size: { type: "property", detail: "number", info: "Number of elements in the set" },
          has: { type: "method", detail: "(value) => boolean", info: "Check if a value is in the set" },
          add: { type: "method", detail: "(value) => Set", info: "Add a value to the set" },
          delete: { type: "method", detail: "(value) => boolean", info: "Remove a value from the set" },
          clear: { type: "method", detail: "() => void", info: "Remove all elements" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each element" },
          values: { type: "method", detail: "() => Iterator", info: "Returns an iterator of values" },
          first: { type: "method", detail: "() => Token5e | undefined", info: "Get the first element (Foundry Set)" }
        }
      },
      hitTargets: {
        type: "property",
        detail: "Set<Token5e>",
        info: "Tokens that were hit",
        children: {
          size: { type: "property", detail: "number", info: "Number of elements in the set" },
          has: { type: "method", detail: "(value) => boolean", info: "Check if a value is in the set" },
          add: { type: "method", detail: "(value) => Set", info: "Add a value to the set" },
          delete: { type: "method", detail: "(value) => boolean", info: "Remove a value from the set" },
          clear: { type: "method", detail: "() => void", info: "Remove all elements" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each element" },
          values: { type: "method", detail: "() => Iterator", info: "Returns an iterator of values" },
          first: { type: "method", detail: "() => Token5e | undefined", info: "Get the first element (Foundry Set)" }
        }
      },
      failedSaves: {
        type: "property",
        detail: "Set<Token5e>",
        info: "Tokens that failed saves",
        children: {
          size: { type: "property", detail: "number", info: "Number of elements in the set" },
          has: { type: "method", detail: "(value) => boolean", info: "Check if a value is in the set" },
          add: { type: "method", detail: "(value) => Set", info: "Add a value to the set" },
          delete: { type: "method", detail: "(value) => boolean", info: "Remove a value from the set" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each element" },
          values: { type: "method", detail: "() => Iterator", info: "Returns an iterator of values" },
          first: { type: "method", detail: "() => Token5e | undefined", info: "Get the first element (Foundry Set)" }
        }
      },
      savedTargets: {
        type: "property",
        detail: "Set<Token5e>",
        info: "Tokens that succeeded saves",
        children: {
          size: { type: "property", detail: "number", info: "Number of elements in the set" },
          has: { type: "method", detail: "(value) => boolean", info: "Check if a value is in the set" },
          add: { type: "method", detail: "(value) => Set", info: "Add a value to the set" },
          delete: { type: "method", detail: "(value) => boolean", info: "Remove a value from the set" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each element" },
          values: { type: "method", detail: "() => Iterator", info: "Returns an iterator of values" },
          first: { type: "method", detail: "() => Token5e | undefined", info: "Get the first element (Foundry Set)" }
        }
      },
      actor: {
        type: "property",
        detail: "Actor5e",
        info: "The D&D 5e actor performing the action",
        children: {
          system: {
            type: "property",
            detail: "object",
            info: "Actor system data",
            children: {
              abilities: { type: "property", detail: "object", info: "Ability scores { str, dex, con, int, wis, cha }" },
              attributes: { type: "property", detail: "object", info: "Actor attributes (hp, ac, movement, etc.)" },
              skills: { type: "property", detail: "object", info: "Skill data" },
              traits: { type: "property", detail: "object", info: "Damage/condition traits (di, dr, dv, ci)" },
              details: { type: "property", detail: "object", info: "Character details (level, cr, race)" },
              spells: { type: "property", detail: "object", info: "Spell slots" }
            }
          },
          items: { type: "property", detail: "Collection<Item5e>", info: "Actor's item collection" },
          effects: { type: "property", detail: "Collection<ActiveEffect5e>", info: "Actor's active effects" },
          classes: { type: "property", detail: "object", info: "Character's class items" },
          rollAbilityTest: { type: "method", detail: "(abilityId, options?) => Promise<D20Roll>", info: "Roll an ability check" },
          rollAbilitySave: { type: "method", detail: "(abilityId, options?) => Promise<D20Roll>", info: "Roll a saving throw" },
          rollSkill: { type: "method", detail: "(skillId, options?) => Promise<D20Roll>", info: "Roll a skill check" },
          applyDamage: { type: "method", detail: "(amount, options?) => Promise<Actor5e>", info: "Apply damage/healing" },
          getRollData: { type: "method", detail: "() => object", info: "Get data for roll formulas" },
          getFlag: { type: "method", detail: "(scope, key) => any", info: "Get a flag value" },
          setFlag: { type: "method", detail: "(scope, key, value) => Promise", info: "Set a flag value" }
        }
      },
      item: {
        type: "property",
        detail: "Item5e",
        info: "The D&D 5e item being used",
        children: {
          system: {
            type: "property",
            detail: "object",
            info: "Item system data",
            children: {
              description: { type: "property", detail: "object", info: "Item description" },
              damage: { type: "property", detail: "object", info: "Damage configuration" },
              save: { type: "property", detail: "object", info: "Save configuration" },
              range: { type: "property", detail: "object", info: "Range configuration" },
              target: { type: "property", detail: "object", info: "Target configuration" },
              duration: { type: "property", detail: "object", info: "Duration configuration" },
              uses: { type: "property", detail: "object", info: "Limited uses" },
              level: { type: "property", detail: "number", info: "Spell level" },
              properties: { type: "property", detail: "Set<string>", info: "Item properties" }
            }
          },
          activities: { type: "property", detail: "Collection<Activity>", info: "Item's activities" },
          effects: { type: "property", detail: "Collection<ActiveEffect>", info: "Item's effects" },
          actor: { type: "property", detail: "Actor5e | null", info: "Owning actor" },
          use: { type: "method", detail: "(config?, dialog?, message?) => Promise<ChatMessage[]>", info: "Use the item" },
          getRollData: { type: "method", detail: "() => object", info: "Get data for roll formulas" },
          getFlag: { type: "method", detail: "(scope, key) => any", info: "Get a flag value" },
          setFlag: { type: "method", detail: "(scope, key, value) => Promise", info: "Set a flag value" }
        }
      },
      activity: {
        type: "property",
        detail: "Activity",
        info: "The D&D 5e activity being used"
      },
      token: {
        type: "property",
        detail: "Token5e",
        info: "The D&D 5e token performing the action",
        children: {
          actor: { type: "property", detail: "Actor5e | null", info: "The actor this token represents" },
          document: { type: "property", detail: "TokenDocument5e", info: "The token document" },
          name: { type: "property", detail: "string", info: "Token display name" },
          center: { type: "property", detail: "{ x, y }", info: "Token center point" },
          visible: { type: "property", detail: "boolean", info: "Whether token is visible" },
          inCombat: { type: "property", detail: "boolean", info: "Whether token is in combat" }
        }
      },
      isCritical: { type: "property", detail: "boolean", info: "Whether the attack is a critical hit" },
      isFumble: { type: "property", detail: "boolean", info: "Whether the attack is a fumble" },
      attackRoll: {
        type: "property",
        detail: "D20Roll",
        info: "The attack roll",
        children: {
          total: { type: "property", detail: "number", info: "The total result of the roll" },
          formula: { type: "property", detail: "string", info: "The roll formula" },
          result: { type: "property", detail: "string", info: "The roll result string" },
          terms: { type: "property", detail: "RollTerm[]", info: "The parsed roll terms" },
          dice: { type: "property", detail: "DiceTerm[]", info: "The dice terms in the roll" },
          isCritical: { type: "property", detail: "boolean", info: "Whether the roll is a natural 20" },
          isFumble: { type: "property", detail: "boolean", info: "Whether the roll is a natural 1" },
          hasAdvantage: { type: "property", detail: "boolean", info: "Whether rolled with advantage" },
          hasDisadvantage: { type: "property", detail: "boolean", info: "Whether rolled with disadvantage" },
          toMessage: { type: "method", detail: "(messageData?, options?) => Promise<ChatMessage>", info: "Send the roll to chat" }
        }
      },
      attackTotal: { type: "property", detail: "number", info: "Attack roll total" },
      damageRolls: {
        type: "property",
        detail: "DamageRoll[]",
        info: "Damage rolls",
        children: {
          length: { type: "property", detail: "number", info: "Number of damage rolls" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each roll" },
          map: { type: "method", detail: "(callback) => Array", info: "Map each roll to a new value" },
          filter: { type: "method", detail: "(callback) => DamageRoll[]", info: "Filter damage rolls" },
          reduce: { type: "method", detail: "(callback, initial?) => any", info: "Reduce damage rolls to a value" },
          find: { type: "method", detail: "(callback) => DamageRoll | undefined", info: "Find a damage roll matching a condition" }
        }
      },
      damageTotal: { type: "property", detail: "number", info: "Total damage" },
      damageDetail: {
        type: "property",
        detail: "object[]",
        info: "Damage breakdown by type",
        children: {
          length: { type: "property", detail: "number", info: "Number of damage entries" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each entry" },
          map: { type: "method", detail: "(callback) => Array", info: "Map each entry to a new value" },
          filter: { type: "method", detail: "(callback) => object[]", info: "Filter damage entries" },
          find: { type: "method", detail: "(callback) => object | undefined", info: "Find a damage entry matching a condition" }
        }
      },
      saveResults: {
        type: "property",
        detail: "Map<string, object>",
        info: "Save results by token UUID",
        children: {
          size: { type: "property", detail: "number", info: "Number of entries in the map" },
          get: { type: "method", detail: "(key) => object | undefined", info: "Get a save result by token UUID" },
          has: { type: "method", detail: "(key) => boolean", info: "Check if a token UUID has a result" },
          set: { type: "method", detail: "(key, value) => Map", info: "Set a save result for a token UUID" },
          delete: { type: "method", detail: "(key) => boolean", info: "Remove a save result" },
          forEach: { type: "method", detail: "(callback) => void", info: "Execute a function for each entry" },
          keys: { type: "method", detail: "() => Iterator", info: "Returns an iterator of keys" },
          values: { type: "method", detail: "() => Iterator", info: "Returns an iterator of values" },
          entries: { type: "method", detail: "() => Iterator", info: "Returns an iterator of [key, value] pairs" }
        }
      },
      templateId: { type: "property", detail: "string", info: "Placed template ID" },
      uuid: { type: "property", detail: "string", info: "Workflow UUID" },
      id: { type: "property", detail: "string", info: "Workflow ID" }
    }
  }
};
var MIDI_SCOPE_MAP = [
  { key: "workflow", detail: "Workflow", info: "The midi-qol workflow instance" },
  { key: "rolledActivity", detail: "Activity", info: "The activity that was rolled/used", ref: "activity" },
  { key: "macroActivity", detail: "Activity", info: "The activity the macro is attached to (may differ from rolledActivity)", ref: "activity" },
  { key: "item", detail: "Item", info: "Item being used" },
  { key: "rolledItem", detail: "Item", info: "The item that was rolled/used (same as item in most cases)", ref: "item" },
  { key: "macroItem", detail: "Item", info: "The item the macro is attached to (may differ from rolledItem for transferred macros)", ref: "item" },
  { key: "args", detail: "any[]", info: "Macro arguments array, args[0] is MidiMacroData" },
  { key: "options", detail: "object", info: "Options passed to callMacro" },
  { key: "midiData", detail: "MidiMacroData", info: "Full midi macro data object (same as args[0] and scope.midiData)" }
];
var MIDI_ARGS0 = {
  type: "property",
  detail: "MidiMacroData",
  info: "Midi-qol macro data (from workflow.getMacroData())",
  children: {
    // Actor info
    actor: { type: "property", detail: "Actor5e", info: "The actor performing the action" },
    actorData: { type: "property", detail: "object", info: "Actor source data (actor.toObject(false))" },
    actorUuid: { type: "property", detail: "string", info: "Actor UUID" },
    // Attack info
    advantage: { type: "property", detail: "boolean", info: "Whether the roll had advantage" },
    disadvantage: { type: "property", detail: "boolean", info: "Whether the roll had disadvantage" },
    attackD20: { type: "property", detail: "number", info: "The natural d20 roll result" },
    attackRoll: { type: "property", detail: "Roll", info: "The attack roll" },
    attackTotal: { type: "property", detail: "number", info: "The attack roll total" },
    diceRoll: { type: "property", detail: "number", info: "The natural d20 roll result (same as attackD20)" },
    isCritical: { type: "property", detail: "boolean", info: "Whether the attack was a critical hit" },
    isFumble: { type: "property", detail: "boolean", info: "Whether the attack was a fumble" },
    // Damage info
    damageDetail: { type: "property", detail: "DamageDescription[]", info: "Damage breakdown by type" },
    damageList: { type: "property", detail: "DamageListEntry[]", info: "Detailed damage list entries" },
    damageRoll: { type: "property", detail: "Roll", info: "The combined damage roll" },
    damageRolls: { type: "property", detail: "Roll[]", info: "Individual damage rolls" },
    damageTotal: { type: "property", detail: "number", info: "Total damage dealt" },
    // Bonus damage
    bonusDamageDetail: { type: "property", detail: "DamageDescription[]", info: "Bonus damage breakdown by type" },
    bonusDamageHTML: { type: "property", detail: "string", info: "Bonus damage HTML display" },
    bonusDamageRoll: { type: "property", detail: "Roll", info: "The combined bonus damage roll" },
    bonusDamageRolls: { type: "property", detail: "Roll[]", info: "Individual bonus damage rolls" },
    bonusDamageTotal: { type: "property", detail: "number", info: "Total bonus damage" },
    // Other damage
    otherDamageDetail: { type: "property", detail: "DamageDescription[]", info: "Other damage breakdown by type" },
    otherDamageList: { type: "property", detail: "DamageListEntry[]", info: "Other damage list entries" },
    otherDamageTotal: { type: "property", detail: "number", info: "Other damage total" },
    // Targets
    targets: { type: "property", detail: "TokenDocument[]", info: "All targeted token documents" },
    targetUuids: { type: "property", detail: "string[]", info: "UUIDs of all targeted tokens" },
    hitTargets: { type: "property", detail: "TokenDocument[]", info: "Token documents that were hit" },
    hitTargetUuids: { type: "property", detail: "string[]", info: "UUIDs of hit targets" },
    hitTargetsEC: { type: "property", detail: "TokenDocument[]", info: "Token documents hit with EC (extra critical)" },
    hitTargetUuidsEC: { type: "property", detail: "string[]", info: "UUIDs of EC hit targets" },
    // Saves
    saves: { type: "property", detail: "TokenDocument[]", info: "Token documents that saved" },
    saveUuids: { type: "property", detail: "string[]", info: "UUIDs of tokens that saved" },
    failedSaves: { type: "property", detail: "TokenDocument[]", info: "Token documents that failed saves" },
    failedSaveUuids: { type: "property", detail: "string[]", info: "UUIDs of tokens that failed saves" },
    superSavers: { type: "property", detail: "TokenDocument[]", info: "Token documents that super-saved (e.g. evasion)" },
    superSaverUuids: { type: "property", detail: "string[]", info: "UUIDs of super savers" },
    semiSuperSavers: { type: "property", detail: "TokenDocument[]", info: "Token documents that semi-super-saved" },
    semiSuperSaverUuids: { type: "property", detail: "string[]", info: "UUIDs of semi-super savers" },
    criticalSaves: { type: "property", detail: "TokenDocument[]", info: "Token documents that critically saved" },
    criticalSaveUuids: { type: "property", detail: "string[]", info: "UUIDs of critical savers" },
    fumbleSaves: { type: "property", detail: "TokenDocument[]", info: "Token documents that fumbled their save" },
    fumbleSaveUuids: { type: "property", detail: "string[]", info: "UUIDs of fumble savers" },
    // Item info
    item: { type: "property", detail: "object", info: "Item source data (item.toObject(false)) with uuid" },
    itemData: { type: "property", detail: "object", info: "Item source data (same as item)" },
    itemUuid: { type: "property", detail: "string", info: "Item UUID" },
    itemCardUuid: { type: "property", detail: "string", info: "Chat card UUID for the item use" },
    // Spell/cast info
    castLevel: { type: "property", detail: "number", info: "The level the spell was cast at" },
    spellLevel: { type: "property", detail: "number", info: "The level the spell was cast at (same as castLevel)" },
    castData: { type: "property", detail: "object", info: "Casting data" },
    // Roll info
    rollData: { type: "property", detail: "object", info: "Roll data for formula evaluation" },
    rollOptions: { type: "property", detail: "object", info: "Workflow roll options" },
    event: { type: "property", detail: "Event", info: "The triggering event (if any)" },
    // Workflow info
    workflow: { type: "property", detail: "Workflow", info: "The midi-qol workflow instance" },
    workflowId: { type: "property", detail: "string", info: "Workflow ID" },
    workflowOptions: { type: "property", detail: "object", info: "Workflow options" },
    id: { type: "property", detail: "string", info: "Workflow ID" },
    // Macro context (added by callMacros/callMacro)
    tag: { type: "property", detail: "string", info: "Macro tag (OnUse, DamageBonus, etc.)" },
    macroPass: { type: "property", detail: "string", info: "Current macro pass (preItemRoll, postActiveEffects, preSave, etc.)" },
    speaker: { type: "property", detail: "object", info: "Chat speaker data" },
    sourceItemUuid: { type: "property", detail: "string", info: "UUID of the source item the macro was found on" },
    options: { type: "property", detail: "object", info: "Options passed to callMacro" },
    // Template/token
    templateId: { type: "property", detail: "string", info: "Placed template ID (deprecated)" },
    templateUuid: { type: "property", detail: "string", info: "Placed template UUID" },
    tokenUuid: { type: "property", detail: "string", info: "Token UUID of the acting token" },
    uuid: { type: "property", detail: "string", info: "Item UUID (deprecated, use itemUuid)" }
  }
};
function injectMidiCompletions() {
  Hooks.on("macro-autocomplete.ready", ({
    tree,
    rebuildSignatureMap,
    schemaToCompletions,
    mergeCompletions,
    classToCompletions,
    registerCategoryConfigs,
    registerBlockColours,
    registerScopeMergeKeys
  }) => {
    console.log("midi-qol | Injecting midi-qol completions into macro-autocomplete");
    registerCategoryConfigs?.({
      activity: { display: "activity", colour: "#e76f51", order: 10 },
      workflow: { display: "workflow", colour: "#e63946", order: 13 },
      rolledItem: { display: "rolledItem", colour: "#6a994e", order: 17 },
      macroItem: { display: "macroItem", colour: "#6a994e", order: 18 },
      rolledActivity: { display: "rolledActivity", colour: "#e76f51", order: 19 },
      macroActivity: { display: "macroActivity", colour: "#e76f51", order: 20 },
      options: { display: "options", colour: "#a5805b", order: 23 },
      midiData: { display: "midiData", colour: "#e63946", order: 24 },
      MidiQOL: { display: "MidiQOL", colour: "#e63946", order: 30 },
      chrisPremades: { display: "Chris Premades", colour: "#e76f51", order: 31, namespaceOnly: true },
      // Condition editor categories
      "Actor Data": { display: "Actor Data", colour: "#457b9d", order: 70 },
      "Target Data": { display: "Target Data", colour: "#2a9d8f", order: 71 },
      "Workflow": { display: "Workflow", colour: "#e63946", order: 72 },
      "World": { display: "World", colour: "#e9c46a", order: 73 },
      "Combat": { display: "Combat", colour: "#5ba580", order: 74 },
      "Functions": { display: "Functions", colour: "#9b59b6", order: 75 }
    });
    registerBlockColours?.({
      MidiQOL: 0,
      workflow: 350
    });
    registerScopeMergeKeys?.(["macroItem", "rolledItem", "macroActivity", "rolledActivity"]);
    const cfg = globalThis.CONFIG;
    tree.MidiQOL = MIDI_COMPLETIONS.MidiQOL;
    tree.workflow = MIDI_COMPLETIONS.workflow;
    if (!tree.item) {
      tree.item = { type: "variable", detail: "Item", info: "Item being used (available in midi-qol macros, spread from scope)", children: {} };
    }
    tree.item.type = "variable";
    tree.item.detail = "Item";
    tree.item.info = "Item being used (available in midi-qol macros, spread from scope)";
    if (mergeCompletions) {
      mergeCompletions(tree.item, {
        name: { type: "property", detail: "string", info: "Item name" },
        type: { type: "property", detail: "string", info: "Item type (weapon, spell, feat, equipment, consumable, ...)" },
        img: { type: "property", detail: "string", info: "Item image path" },
        uuid: { type: "property", detail: "string", info: "Item UUID" },
        id: { type: "property", detail: "string", info: "Item ID" },
        isOwner: { type: "property", detail: "boolean", info: "Current user is owner" },
        items: { type: "property", detail: "Collection<Item>", info: "Actor's item collection" },
        effects: { type: "property", detail: "Collection<ActiveEffect>", info: "Item's effects" },
        actor: { type: "property", detail: "Actor | null", info: "Owning actor" }
      });
    }
    const ACTIVITY_CHILDREN = {
      // Core identification
      name: { type: "property", detail: "string", info: "Activity name" },
      type: { type: "property", detail: "string", info: "Activity type (e.g. 'attack', 'save', 'damage', 'heal', 'utility')" },
      identifier: { type: "property", detail: "string", info: "Unique identifier (custom or name slugified)" },
      img: { type: "property", detail: "string", info: "Activity image path" },
      uuid: { type: "property", detail: "string", info: "Activity UUID" },
      sort: { type: "property", detail: "number", info: "Sort order" },
      // Relationships
      item: {
        type: "property",
        detail: "Item5e",
        info: "Parent item",
        children: {
          system: {
            type: "property",
            detail: "object",
            info: "Item system data",
            children: {
              description: { type: "property", detail: "object", info: "Item description" },
              damage: { type: "property", detail: "object", info: "Damage configuration" },
              save: { type: "property", detail: "object", info: "Save configuration" },
              range: { type: "property", detail: "object", info: "Range configuration" },
              target: { type: "property", detail: "object", info: "Target configuration" },
              duration: { type: "property", detail: "object", info: "Duration configuration" },
              uses: { type: "property", detail: "object", info: "Limited uses" },
              level: { type: "property", detail: "number", info: "Spell level" },
              properties: { type: "property", detail: "Set<string>", info: "Item properties" }
            }
          },
          activities: { type: "property", detail: "Collection<Activity>", info: "Item's activities" },
          effects: { type: "property", detail: "Collection<ActiveEffect>", info: "Item's effects" },
          actor: { type: "property", detail: "Actor5e | null", info: "Owning actor" },
          name: { type: "property", detail: "string", info: "Item name" },
          type: { type: "property", detail: "string", info: "Item type" },
          img: { type: "property", detail: "string", info: "Item image path" },
          uuid: { type: "property", detail: "string", info: "Item UUID" }
        }
      },
      actor: {
        type: "property",
        detail: "Actor5e",
        info: "Owning actor (via item.actor)",
        children: {
          system: {
            type: "property",
            detail: "object",
            info: "Actor system data",
            children: {
              abilities: { type: "property", detail: "object", info: "Ability scores { str, dex, con, int, wis, cha }" },
              attributes: { type: "property", detail: "object", info: "Actor attributes (hp, ac, movement, etc.)" },
              skills: { type: "property", detail: "object", info: "Skill data" },
              traits: { type: "property", detail: "object", info: "Damage/condition traits (di, dr, dv, ci)" },
              details: { type: "property", detail: "object", info: "Character details (level, cr, race)" },
              spells: { type: "property", detail: "object", info: "Spell slots" }
            }
          },
          items: { type: "property", detail: "Collection<Item5e>", info: "Actor's item collection" },
          effects: { type: "property", detail: "Collection<ActiveEffect5e>", info: "Actor's active effects" },
          name: { type: "property", detail: "string", info: "Actor name" },
          uuid: { type: "property", detail: "string", info: "Actor UUID" },
          getRollData: { type: "method", detail: "() => object", info: "Get data for roll formulas" },
          getFlag: { type: "method", detail: "(scope, key) => any", info: "Get a flag value" },
          setFlag: { type: "method", detail: "(scope, key, value) => Promise", info: "Set a flag value" }
        }
      },
      // Type checks
      actionType: { type: "property", detail: "string", info: "Action type (e.g. 'mwak', 'rwak', 'msak', 'rsak', 'save', 'heal', 'utility')" },
      hasDamage: { type: "property", detail: "boolean", info: "Whether activity has a damage roll" },
      hasHealing: { type: "property", detail: "boolean", info: "Whether activity has healing" },
      hasAttack: { type: "property", detail: "boolean", info: "Whether activity has an attack roll" },
      hasSave: { type: "property", detail: "boolean", info: "Whether activity has a saving throw" },
      // Data sections
      activation: {
        type: "property",
        detail: "object",
        info: "Activation configuration",
        children: {
          type: { type: "property", detail: "string", info: "Activation type (action, bonus, reaction, etc.)" },
          value: { type: "property", detail: "number", info: "Activation value" },
          condition: { type: "property", detail: "string", info: "Activation condition text" }
        }
      },
      consumption: {
        type: "property",
        detail: "object",
        info: "Consumption configuration",
        children: {
          targets: { type: "property", detail: "object[]", info: "Consumption targets" },
          scaling: { type: "property", detail: "object", info: "Consumption scaling" }
        }
      },
      description: {
        type: "property",
        detail: "object",
        info: "Description data",
        children: {
          chatFlavor: { type: "property", detail: "string", info: "Chat flavor text" }
        }
      },
      duration: {
        type: "property",
        detail: "object",
        info: "Duration configuration",
        children: {
          value: { type: "property", detail: "number", info: "Duration value" },
          units: { type: "property", detail: "string", info: "Duration units (turn, round, minute, hour, day, etc.)" },
          special: { type: "property", detail: "string", info: "Special duration text" },
          concentration: { type: "property", detail: "boolean", info: "Requires concentration" }
        }
      },
      range: {
        type: "property",
        detail: "object",
        info: "Range configuration",
        children: {
          value: { type: "property", detail: "number", info: "Range value" },
          units: { type: "property", detail: "string", info: "Range units" },
          special: { type: "property", detail: "string", info: "Special range text" },
          override: { type: "property", detail: "boolean", info: "Override item range" }
        }
      },
      target: {
        type: "property",
        detail: "object",
        info: "Target configuration",
        children: {
          template: {
            type: "property",
            detail: "object",
            info: "Area template configuration",
            children: {
              type: { type: "property", detail: "string", info: "Template shape (cone, cube, cylinder, line, radius, sphere, square, wall)" },
              size: { type: "property", detail: "number", info: "Template size" },
              units: { type: "property", detail: "string", info: "Template units" },
              contiguous: { type: "property", detail: "boolean", info: "Must be contiguous" }
            }
          },
          affects: {
            type: "property",
            detail: "object",
            info: "Affected targets",
            children: {
              type: { type: "property", detail: "string", info: "Target type (creature, ally, enemy, self, willing, etc.)" },
              count: { type: "property", detail: "string", info: "Number of targets" },
              special: { type: "property", detail: "string", info: "Special targeting text" }
            }
          },
          prompt: { type: "property", detail: "boolean", info: "Prompt for targets" }
        }
      },
      uses: {
        type: "property",
        detail: "object",
        info: "Limited uses",
        children: {
          spent: { type: "property", detail: "number", info: "Uses spent" },
          max: { type: "property", detail: "string", info: "Maximum uses (formula)" },
          per: { type: "property", detail: "string", info: "Recovery period" },
          recovery: { type: "property", detail: "object[]", info: "Recovery profiles" }
        }
      },
      damage: {
        type: "property",
        detail: "object",
        info: "Damage configuration",
        children: {
          parts: { type: "property", detail: "object[]", info: "Damage parts (formula, type, scaling)" }
        }
      },
      healing: {
        type: "property",
        detail: "object",
        info: "Healing configuration",
        children: {
          types: { type: "property", detail: "Set<string>", info: "Healing types" }
        }
      },
      save: {
        type: "property",
        detail: "object",
        info: "Save configuration",
        children: {
          ability: { type: "property", detail: "Set<string>", info: "Save ability/abilities" },
          dc: {
            type: "property",
            detail: "object",
            info: "Save DC",
            children: {
              calculation: { type: "property", detail: "string", info: "DC calculation method" },
              formula: { type: "property", detail: "string", info: "DC formula" }
            }
          }
        }
      },
      check: {
        type: "property",
        detail: "object",
        info: "Check configuration",
        children: {
          ability: { type: "property", detail: "string", info: "Check ability" },
          associated: { type: "property", detail: "Set<string>", info: "Associated skills" },
          dc: {
            type: "property",
            detail: "object",
            info: "Check DC",
            children: {
              calculation: { type: "property", detail: "string", info: "DC calculation method" },
              formula: { type: "property", detail: "string", info: "DC formula" }
            }
          }
        }
      },
      attack: {
        type: "property",
        detail: "object",
        info: "Attack configuration",
        children: {
          ability: { type: "property", detail: "string", info: "Attack ability" },
          bonus: { type: "property", detail: "string", info: "Attack bonus formula" },
          type: {
            type: "property",
            detail: "object",
            info: "Attack type",
            children: {
              value: { type: "property", detail: "string", info: "Attack type (melee or ranged)" },
              classification: { type: "property", detail: "string", info: "Classification (weapon or spell)" }
            }
          },
          flat: { type: "property", detail: "boolean", info: "Flat attack bonus (no proficiency/ability)" }
        }
      },
      // Effects
      effects: { type: "property", detail: "ActivityEffectData[]", info: "Activity effect data entries" },
      applicableEffects: { type: "property", detail: "ActiveEffect[]", info: "Active effects applicable from this activity" },
      // Conditions (midi-qol MidiActivityMixin getters)
      useCondition: { type: "property", detail: "string", info: "Use activation condition text" },
      effectCondition: { type: "property", detail: "string", info: "Effect activation condition text" },
      otherActivity: { type: "property", detail: "Activity | null", info: "Associated 'other activity' (ammunition, triggered, etc.)" },
      // Midi-specific
      macro: { type: "property", detail: "Macro", info: "Activity on-use macro" },
      midiProperties: {
        type: "property",
        detail: "object",
        info: "Midi-qol activity properties",
        children: {
          ignoreTraits: { type: "property", detail: "Set<string>", info: "Ignored damage traits (idi, idr, idv, ida)" },
          triggeredActivityId: { type: "property", detail: "string", info: "Triggered activity ID" },
          triggeredActivityConditionText: { type: "property", detail: "string", info: "Triggered activity condition" },
          triggeredActivityTargets: { type: "property", detail: "string", info: "Triggered activity target mode" },
          triggeredActivityRollAs: { type: "property", detail: "string", info: "Triggered activity roll-as mode" },
          autoConsume: { type: "property", detail: "boolean", info: "Auto-consume resources" },
          confirmTargets: { type: "property", detail: "string", info: "Target confirmation mode" },
          autoTargetType: { type: "property", detail: "string", info: "Auto-targeting type" },
          automationOnly: { type: "property", detail: "boolean", info: "Hidden from manual use (automation only)" },
          otherActivityCompatible: { type: "property", detail: "boolean", info: "Can be used as 'other activity'" },
          identifier: { type: "property", detail: "string", info: "Custom identifier" },
          displayActivityName: { type: "property", detail: "boolean", info: "Show activity name on chat card" },
          rollMode: { type: "property", detail: "string", info: "Roll mode override" },
          chooseEffects: { type: "property", detail: "boolean", info: "Prompt to choose which effects to apply" },
          toggleEffect: { type: "property", detail: "boolean", info: "Toggle effect on/off" },
          magicEffect: { type: "property", detail: "boolean", info: "Effects are magical" },
          magicDamage: { type: "property", detail: "boolean", info: "Damage is magical" },
          noConcentrationCheck: { type: "property", detail: "boolean", info: "Skip concentration check" }
        }
      }
    };
    tree.activity = {
      type: "variable",
      detail: "Activity",
      info: "Activity being used (available in midi-qol macros, spread from scope)",
      children: ACTIVITY_CHILDREN
    };
    tree.Activity = {
      type: "class",
      detail: "Activity",
      info: "dnd5e Activity base class",
      instanceChildren: ACTIVITY_CHILDREN
    };
    if (tree.workflow?.children?.activity) {
      tree.workflow.children.activity.children = ACTIVITY_CHILDREN;
    }
    tree.Workflow = {
      type: "class",
      detail: "Workflow",
      info: "midi-qol Workflow class",
      instanceChildren: tree.workflow?.children ?? {}
    };
    if (classToCompletions && mergeCompletions) {
      const itemCls = cfg?.Item?.documentClass;
      if (itemCls) {
        const walked = classToCompletions(itemCls);
        if (!tree.item.instanceChildren) tree.item.instanceChildren = {};
        mergeCompletions({ children: tree.item.instanceChildren }, walked.instanceChildren);
      }
      const activityTypes = cfg?.DND5E?.activityTypes;
      if (activityTypes && typeof activityTypes === "object") {
        for (const entry of Object.values(activityTypes)) {
          const ActCls = entry?.documentClass;
          if (ActCls?.prototype) {
            const walked = classToCompletions(ActCls);
            if (!tree.activity.instanceChildren) tree.activity.instanceChildren = {};
            mergeCompletions({ children: tree.activity.instanceChildren }, walked.instanceChildren);
            if (!tree.Activity.instanceChildren) tree.Activity.instanceChildren = {};
            mergeCompletions({ children: tree.Activity.instanceChildren }, walked.instanceChildren);
          }
        }
      }
    }
    if (schemaToCompletions && mergeCompletions) {
      const itemDataModels = cfg?.Item?.dataModels;
      if (itemDataModels && typeof itemDataModels === "object" && tree.item?.children?.system) {
        for (const [, DataModel] of Object.entries(itemDataModels)) {
          if (DataModel?.schema) {
            const sc = schemaToCompletions(DataModel.schema);
            mergeCompletions(tree.item.children.system, sc);
          }
        }
      }
    }
    if (tree.args?.children) {
      tree.args.children["0"] = MIDI_ARGS0;
    }
    tree.midiData = {
      type: "variable",
      detail: "MidiMacroData",
      info: "Full midi macro data object (same as args[0] and scope.midiData)",
      children: MIDI_ARGS0.children
    };
    for (const { key, detail, info, ref } of MIDI_SCOPE_MAP) {
      const source = ref ? tree[ref] : void 0;
      if (tree.scope?.children) {
        tree.scope.children[key] = {
          type: "property",
          detail,
          info,
          ...source?.children && { children: source.children },
          ...source?.instanceChildren && { instanceChildren: source.instanceChildren }
        };
      }
      if (!tree[key]) {
        tree[key] = {
          type: "variable",
          detail,
          info,
          ...source?.children && { children: source.children },
          ...source?.instanceChildren && { instanceChildren: source.instanceChildren }
        };
      }
    }
    if (mergeCompletions && globalThis.chrisPremades) {
      const fn = /* @__PURE__ */ __name((detail, info) => ({ type: "function", detail, info }), "fn");
      const prop = /* @__PURE__ */ __name((detail, info) => ({ type: "property", detail, info }), "prop");
      const method = /* @__PURE__ */ __name((detail, info) => ({ type: "method", detail, info }), "method");
      const CPR_UTILS = {
        actorUtils: {
          type: "object",
          detail: "object",
          info: "Actor utility functions",
          children: {
            getEffects: fn("(actor, options?) => ActiveEffect[]", "Get effects on an actor"),
            addFavorites: fn("async (actor, entities, type?) => Promise", "Add items to favorites"),
            removeFavorites: fn("async (actor, entities, type?) => Promise", "Remove items from favorites"),
            getFirstToken: fn("(actor) => Token5e", "Get first token for an actor"),
            getLevelOrCR: fn("(actor) => number", "Get character level or CR"),
            checkTrait: fn("(actor, type, trait) => boolean", "Check if actor has a damage/condition trait"),
            typeOrRace: fn("(actor) => string", "Get creature type or race"),
            raceOrType: fn("(actor) => string", "Get creature race or type"),
            getSize: fn("(actor, returnString?) => number|string", "Get creature size"),
            hasUsedReaction: fn("(actor) => boolean", "Check if actor has used reaction"),
            setReactionUsed: fn("async (actor) => Promise", "Mark reaction as used"),
            removeReactionUsed: fn("async (actor, force?) => Promise", "Clear reaction used"),
            hasUsedBonusAction: fn("(actor) => boolean", "Check if bonus action used"),
            setBonusActionUsed: fn("async (actor) => Promise", "Mark bonus action used"),
            removeBonusActionUsed: fn("async (actor, force?) => Promise", "Clear bonus action used"),
            hasSpellSlots: fn("(actor, atLeast?) => boolean", "Check for available spell slots"),
            getCastableSpells: fn("(actor) => Item[]", "Get spells the actor can cast"),
            isShapeChanger: fn("(actor) => boolean", "Check if actor is a shape changer"),
            doConcentrationCheck: fn("async (actor, saveDC) => Promise", "Force a concentration check"),
            polymorph: fn("async (origActor, newActor, options, renderSheet?) => Promise", "Polymorph an actor"),
            getEquippedArmor: fn("(actor, types?) => Item|undefined", "Get equipped armor"),
            getEquippedShield: fn("(actor) => Item|undefined", "Get equipped shield"),
            compareSize: fn("(source, target, goal) => boolean", "Compare creature sizes"),
            getBestAbility: fn("(actor, abilities) => string", "Get highest ability from list"),
            getTokens: fn("(actor) => Token5e[]", "Get all tokens for an actor"),
            giveHeroicInspiration: fn("async (actor) => Promise", "Give heroic inspiration")
          }
        },
        effectUtils: {
          type: "object",
          detail: "object",
          info: "Effect utility functions",
          children: {
            createEffect: fn("async (entity, effectData, ...) => Promise<ActiveEffect>", "Create an active effect"),
            createEffects: fn("async (entity, effectDataArray, optionsArray) => Promise", "Create multiple effects"),
            getConcentrationEffect: fn("(actor, item) => ActiveEffect|undefined", "Get concentration effect for item"),
            getEffectByIdentifier: fn("(actor, identifier) => ActiveEffect|undefined", "Get effect by CPR identifier"),
            getAllEffectsByIdentifier: fn("(actor, identifier) => ActiveEffect[]", "Get all effects by identifier"),
            getEffectByStatusID: fn("(actor, statusID) => ActiveEffect|undefined", "Get effect by status ID"),
            applyConditions: fn("async (actor, conditions, ...) => Promise", "Apply conditions to actor"),
            addDependent: fn("async (entity, dependents, forceGM?) => Promise", "Add dependent entities"),
            addMacro: fn("(effectData, type, macroList) => void", "Add embedded macro to effect data"),
            getCastData: fn("(effect) => object", "Get cast data stored on effect"),
            getCastLevel: fn("(effect) => number", "Get cast level from effect"),
            getBaseLevel: fn("(effect) => number", "Get base spell level from effect"),
            setCastData: fn("async (effect, data) => Promise", "Set cast data on effect"),
            setCastLevel: fn("async (effect, level) => Promise", "Set cast level on effect"),
            getSaveDC: fn("(effect) => number", "Get save DC stored on effect"),
            setSaveDC: fn("async (effect, dc) => Promise", "Set save DC on effect"),
            syntheticActiveEffect: fn("async (effectData, entity) => Promise<ActiveEffect>", "Create synthetic effect"),
            getOriginItem: fn("async (effect) => Promise<Item>", "Get origin item of effect"),
            getOriginItemSync: fn("(effect) => Item|undefined", "Get origin item synchronously"),
            getConditions: fn("(effect) => string[]", "Get conditions from effect")
          }
        },
        itemUtils: {
          type: "object",
          detail: "object",
          info: "Item utility functions",
          children: {
            createItems: fn("async (actor, updates, ...) => Promise<Item[]>", "Create items on actor"),
            getItemByIdentifier: fn("(actor, identifier) => Item|undefined", "Get item by CPR identifier"),
            getAllItemsByIdentifier: fn("(actor, identifier) => Item[]", "Get all items by identifier"),
            syntheticItem: fn("async (itemData, actor) => Promise<Item>", "Create a synthetic (temporary) item"),
            enchantItem: fn("async (item, effectData, ...) => Promise", "Apply enchantment to item"),
            cloneItem: fn("(item, updates?) => Item", "Clone an item with optional updates"),
            getConfig: fn("(item, key) => any", "Get CPR config value from item"),
            setConfig: fn("async (item, key, value) => Promise", "Set CPR config value on item"),
            getSaveDC: fn("(item) => number", "Get save DC for item"),
            getVersion: fn("(item) => string", "Get CPR automation version"),
            getSource: fn("(item) => string", "Get CPR automation source"),
            isUpToDate: fn("async (item) => Promise<boolean>", "Check if automation is up to date"),
            isSpellFeature: fn("(item) => boolean", "Check if item is a spell feature"),
            convertDuration: fn("(entity) => object", "Convert duration to seconds"),
            getEquipmentState: fn("(item) => string", "Get equipment state"),
            getActivity: fn("(item, type) => Activity|undefined", "Get activity by type"),
            getEffectByIdentifier: fn("(item, identifier) => ActiveEffect|undefined", "Get effect on item by identifier"),
            getSpellActivities: fn("(item) => Activity[]", "Get spell-related activities"),
            canUse: fn("(item) => boolean", "Check if item can be used")
          }
        },
        tokenUtils: {
          type: "object",
          detail: "object",
          info: "Token utility functions",
          children: {
            getDistance: fn("(sourceToken, targetToken, ...) => number", "Get distance between tokens"),
            findNearby: fn("(token, range, disposition, ...) => Token5e[]", "Find nearby tokens"),
            pushToken: fn("async (sourceToken, targetToken, distance) => Promise", "Push token away"),
            canSee: fn("(sourceToken, targetToken) => boolean", "Check line of sight"),
            canSense: fn("(sourceToken, targetToken, senseModes?) => boolean", "Check if token can sense another"),
            checkIncapacitated: fn("(token, logResult?) => boolean", "Check if token is incapacitated"),
            checkForRoom: fn("(token, gridSquares) => boolean", "Check if room exists for token"),
            attachToToken: fn("async (token, uuidsToAttach) => Promise", "Attach entities to token"),
            detachFromToken: fn("async (token, uuidsToDetach) => Promise", "Detach entities from token"),
            getLightLevel: fn("(token) => string", "Get light level at token position"),
            grappleHelper: fn("async (sourceToken, targetToken, item, ...) => Promise", "Handle grapple mechanics"),
            moveTokenAlongRay: fn("async (targetToken, ray, distance) => Promise", "Move token along ray"),
            getMovementHitTokens: fn("(startPoint, endPoint, radius, ...) => Token5e[]", "Get tokens hit during movement"),
            getLinearDistanceMoved: fn("(token) => number", "Get linear distance moved this turn"),
            isGrappledBy: fn("(target, source) => boolean", "Check if target is grappled by source"),
            mountToken: fn("async (rider, target, ...) => Promise", "Mount a token on another"),
            checkCover: fn("(sourceToken, targetToken, ...) => number", "Check cover between tokens"),
            getBaseActor: fn("(token) => Actor5e", "Get base actor (unlinked original)")
          }
        },
        workflowUtils: {
          type: "object",
          detail: "object",
          info: "Workflow utility functions",
          children: {
            bonusDamage: fn("async (workflow, formula, ...) => Promise", "Add bonus damage to workflow"),
            bonusAttack: fn("async (workflow, formula) => Promise", "Add bonus to attack roll"),
            replaceDamage: fn("async (workflow, formula, ...) => Promise", "Replace workflow damage"),
            applyDamage: fn("async (tokens, value, damageType) => Promise", "Apply damage to tokens"),
            completeActivityUse: fn("async (activity, config?) => Promise", "Complete an activity use programmatically"),
            completeItemUse: fn("async (item, config?) => Promise", "Complete an item use programmatically"),
            syntheticActivityRoll: fn("async (activity, targets?, ...) => Promise", "Roll synthetic activity"),
            syntheticItemRoll: fn("async (item, targets, ...) => Promise", "Roll synthetic item"),
            syntheticItemDataRoll: fn("async (itemData, actor, targets, ...) => Promise", "Roll from item data"),
            syntheticActivityDataRoll: fn("async (activityData, item, actor, targets, ...) => Promise", "Roll from activity data"),
            negateDamageItemDamage: fn("(ditem) => void", "Negate all damage on a damage item"),
            setDamageItemDamage: fn("(ditem, amount, adjustRaw?) => void", "Set damage item damage amount"),
            getDamageTypes: fn("(damageRolls) => string[]", "Get damage types from rolls"),
            getTotalDamageOfType: fn("(damageDetail, actor, type) => number", "Get total damage of a type"),
            getCastData: fn("(workflow) => object", "Get cast data from workflow"),
            getCastLevel: fn("(workflow) => number", "Get cast level from workflow"),
            modifyDamageAppliedFlat: fn("(ditem, amount) => void", "Modify damage applied by flat amount"),
            handleInstantTemplate: fn("async (workflow) => Promise", "Handle instant template placement"),
            isAttackType: fn("(workflow, type?) => boolean", "Check workflow attack type"),
            getActionType: fn("(workflow) => string", "Get workflow action type"),
            swapAttackAbility: fn("async (workflow, ability?, ...) => Promise", "Swap attack ability modifier"),
            addEntityRemoval: fn("(workflow, entities) => void", "Mark entities for removal after workflow"),
            preventDeath: fn("(ditem) => void", "Prevent target death from this damage"),
            specialItemUse: fn("async (item, targets, sourceFeature, ...) => Promise", "Special item use flow"),
            updateTargets: fn("async (workflow, targets, ...) => Promise", "Update workflow targets"),
            removeTargets: fn("async (workflow, tokens, ...) => Promise", "Remove tokens from targets")
          }
        },
        rollUtils: {
          type: "object",
          detail: "object",
          info: "Roll utility functions",
          children: {
            contestedRoll: fn("async (...) => Promise", "Perform a contested roll"),
            rollDice: fn("async (formula, ...) => Promise<Roll>", "Roll a dice formula"),
            damageRoll: fn("async (formula, entity, options?) => Promise<DamageRoll>", "Roll damage"),
            getCriticalFormula: fn("async (formula, rollData) => Promise<string>", "Get critical hit formula"),
            getChangedDamageRoll: fn("async (origRoll, newType) => Promise<DamageRoll>", "Change damage roll type"),
            addToRoll: fn("async (roll, formula, ...) => Promise<Roll>", "Add formula to existing roll"),
            requestRoll: fn("async (token, request, ability, options?) => Promise", "Request a roll from player"),
            makeCritical: fn("(roll) => Roll", "Convert a roll to critical"),
            rollDiceSync: fn("(formula, ...) => Roll", "Roll dice synchronously"),
            replaceD20: fn("async (roll, number) => Promise<Roll>", "Replace d20 result")
          }
        },
        dialogUtils: {
          type: "object",
          detail: "object",
          info: "Dialog utility functions",
          children: {
            buttonDialog: fn("async (title, content, buttons, ...) => Promise<string>", "Show button dialog"),
            numberDialog: fn("async (title, content, input?) => Promise<number>", "Show number input dialog"),
            selectDialog: fn("async (title, content, input?) => Promise<string>", "Show select dialog"),
            selectTargetDialog: fn("async (title, content, targets, ...) => Promise", "Show target selection dialog"),
            selectDocumentDialog: fn("async (title, content, documents, ...) => Promise", "Show document selection dialog"),
            selectDocumentsDialog: fn("async (title, content, documents, ...) => Promise", "Show multi-document selection"),
            confirm: fn("async (title, content, ...) => Promise<boolean>", "Show confirmation dialog"),
            selectHitDie: fn("async (actor, title, content, ...) => Promise", "Show hit die selection"),
            selectSpellSlot: fn("async (actor, title, content, ...) => Promise<number>", "Show spell slot selection"),
            selectDamageType: fn("async (damageTypes, title, ...) => Promise<string>", "Show damage type selection"),
            confirmUseItem: fn("async (item, ...) => Promise<boolean>", "Confirm item use")
          }
        },
        combatUtils: {
          type: "object",
          detail: "object",
          info: "Combat utility functions",
          children: {
            currentTurn: fn("() => Combatant|undefined", "Get current turn combatant"),
            inCombat: fn("() => boolean", "Check if combat is active"),
            combatStarted: fn("() => boolean", "Check if combat has started"),
            perTurnCheck: fn("(entity, name, ownTurnOnly, tokenId) => boolean", "Check once-per-turn usage"),
            setTurnCheck: fn("async (entity, name, reset) => Promise", "Set per-turn usage flag"),
            getCurrentCombatantToken: fn("() => Token5e|undefined", "Get current combatant's token"),
            isOwnTurn: fn("(token) => boolean", "Check if it's this token's turn")
          }
        },
        templateUtils: {
          type: "object",
          detail: "object",
          info: "Template utility functions",
          children: {
            getTokensInTemplate: fn("(template) => Token5e[]", "Get tokens within a template"),
            getTokensInShape: fn("(shape, scene, ...) => Token5e[]", "Get tokens in a shape"),
            placeTemplate: fn("async (templateData, returnTokens?) => Promise", "Place a measured template"),
            getCastData: fn("(template) => object", "Get cast data from template"),
            getCastLevel: fn("(template) => number", "Get cast level from template"),
            getSaveDC: fn("(template) => number", "Get save DC from template"),
            getSourceActor: fn("async (template) => Promise<Actor5e>", "Get source actor of template"),
            overlap: fn("(template1, template2) => boolean", "Check if two templates overlap"),
            attachToTemplate: fn("async (template, uuids) => Promise", "Attach entities to template"),
            rayIntersectsTemplate: fn("(templateDoc, ray) => boolean", "Check if ray intersects template")
          }
        },
        socketUtils: {
          type: "object",
          detail: "object",
          info: "Socket utility functions",
          children: {
            gmID: fn("() => string", "Get the GM's user ID"),
            isTheGM: fn("() => boolean", "Check if current user is the GM"),
            hasPermission: fn("(entity, userId) => boolean", "Check user permission on entity"),
            firstOwner: fn("(document, useId) => User", "Get first owner of document"),
            remoteRollItem: fn("async (item, config, options, userId) => Promise", "Roll item as another user")
          }
        },
        regionUtils: {
          type: "object",
          detail: "object",
          info: "Region utility functions",
          children: {
            createRegions: fn("async (regionDatas, scene, ...) => Promise", "Create scene regions"),
            templateToRegionShape: fn("(template, ...) => object", "Convert template to region shape"),
            getCastData: fn("(region) => object", "Get cast data from region"),
            getCastLevel: fn("(region) => number", "Get cast level from region"),
            getSaveDC: fn("(region) => number", "Get save DC from region"),
            rayIntersectsRegion: fn("(region, ray) => boolean", "Check if ray intersects region")
          }
        },
        macroUtils: {
          type: "object",
          detail: "object",
          info: "Embedded macro utility functions",
          children: {
            getEmbeddedMacros: fn("(entity, type, options?) => object[]", "Get embedded macros on entity"),
            addEmbeddedMacro: fn("async (entity, macroData) => Promise", "Add embedded macro to entity"),
            removeEmbeddedMacro: fn("async (entity, type, name) => Promise", "Remove embedded macro"),
            getDocumentPasses: fn("(document, type) => string[]", "Get valid passes for doc+event type"),
            getEventTypes: fn("() => string[]", "Get all event type keys"),
            getAllDocumentPasses: fn("(document) => object", "Get all passes for all event types"),
            getAllEmbeddedMacros: fn("(entity) => object[]", "Get all embedded macros")
          }
        },
        activityUtils: {
          type: "object",
          detail: "object",
          info: "Activity utility functions",
          children: {
            getActivityByIdentifier: fn("(item, identifier, ...) => Activity|undefined", "Get activity by identifier"),
            getIdentifier: fn("(activity) => string", "Get CPR identifier of activity"),
            setIdentifier: fn("async (activity, identifier) => Promise", "Set CPR identifier"),
            withChangedDamage: fn("(activity, formula, types?, ...) => Activity", "Clone activity with changed damage"),
            duplicateActivity: fn("(activity) => Activity", "Duplicate an activity"),
            getSaveDC: fn("(activity) => number", "Get save DC of activity"),
            hasSave: fn("(activity) => boolean", "Check if activity has a save"),
            isSpellActivity: fn("(activity) => boolean", "Check if activity belongs to a spell"),
            canUse: fn("(activity) => boolean", "Check if activity can be used"),
            isHidden: fn("(activity) => boolean", "Check if activity is hidden")
          }
        },
        genericUtils: {
          type: "object",
          detail: "object",
          info: "General utility functions",
          children: {
            sleep: fn("(ms) => Promise<void>", "Sleep for ms milliseconds"),
            translate: fn("(key) => string", "Localize a string key"),
            format: fn("(key, obj) => string", "Format a localized string"),
            setProperty: fn("(object, key, value) => boolean", "Set a nested property"),
            getProperty: fn("(object, key) => any", "Get a nested property"),
            duplicate: fn("(object) => object", "Deep duplicate an object"),
            deepClone: fn("(object) => object", "Deep clone an object"),
            mergeObject: fn("(original, other, options?) => object", "Merge objects"),
            update: fn("async (entity, updates?, options?) => Promise", "Update entity (GM-safe)"),
            remove: fn("async (entity) => Promise", "Delete entity (GM-safe)"),
            setFlag: fn("async (entity, scope, key, value) => Promise", "Set flag (GM-safe)"),
            unsetFlag: fn("async (entity, scope, key) => Promise", "Unset flag (GM-safe)"),
            getCPRSetting: fn("(key) => any", "Get a CPR module setting"),
            setCPRSetting: fn("async (key, value) => Promise", "Set a CPR module setting"),
            randomID: fn("(length?) => string", "Generate random ID"),
            notify: fn("(message, type?, ...) => void", "Show notification"),
            getIdentifier: fn("(entity) => string", "Get CPR identifier from entity"),
            getRules: fn("(entity) => string", "Get rules version (legacy/modern)"),
            titleCase: fn("(inputString) => string", "Convert string to title case"),
            createEmbeddedDocuments: fn("async (entity, type, updates, options) => Promise", "Create embedded docs (GM-safe)"),
            updateEmbeddedDocuments: fn("async (entity, type, updates, options) => Promise", "Update embedded docs (GM-safe)"),
            deleteEmbeddedDocuments: fn("async (entity, type, ids, options) => Promise", "Delete embedded docs (GM-safe)")
          }
        },
        compendiumUtils: {
          type: "object",
          detail: "object",
          info: "Compendium utility functions",
          children: {
            getCPRAutomation: fn("async (item, ...) => Promise<object>", "Get CPR automation for item"),
            getAllAutomations: fn("async (item, options?) => Promise<object[]>", "Get all automations for item"),
            getItemFromCompendium: fn("async (key, name, ...) => Promise<Item>", "Get item from compendium"),
            getPreferredAutomation: fn("async (item, options) => Promise<object>", "Get preferred automation"),
            getActorFromCompendium: fn("async (key, name, ...) => Promise<Actor>", "Get actor from compendium")
          }
        },
        constants: {
          type: "object",
          detail: "object",
          info: "CPR constants and lookup tables",
          children: {
            packs: prop("object", "Compendium pack keys"),
            attacks: prop("string[]", "All attack types"),
            meleeAttacks: prop("string[]", "Melee attack types"),
            rangedAttacks: prop("string[]", "Ranged attack types"),
            weaponAttacks: prop("string[]", "Weapon attack types"),
            spellAttacks: prop("string[]", "Spell attack types"),
            sizes: prop("object", "Creature size mappings"),
            damageTypeOptions: prop("object[]", "Damage type select options"),
            creatureTypeOptions: prop("object[]", "Creature type select options"),
            itemTypes: prop("string[]", "Valid item types"),
            abilityOptions: prop("object[]", "Ability score select options"),
            statusOptions: prop("object[]", "Status condition options"),
            skillOptions: prop("object[]", "Skill select options"),
            overTimeOptions: prop("object[]", "Overtime effect options"),
            getBaseWeaponOptions: fn("() => object[]", "Get base weapon options"),
            getToolNames: fn("() => object[]", "Get tool proficiency names"),
            getFeatOptions: fn("() => object[]", "Get feat options")
          }
        },
        errors: {
          type: "object",
          detail: "object",
          info: "Error helper functions",
          children: {
            missingPack: fn("() => void", "Log missing pack error"),
            missingPackItem: fn("(key, name) => void", "Log missing pack item error"),
            missingActivity: fn("(identifier) => void", "Log missing activity error")
          }
        },
        animationUtils: {
          type: "object",
          detail: "object",
          info: "Animation utility functions (Sequencer/JB2A)",
          children: {
            sequencerCheck: fn("() => boolean", "Check if Sequencer is available"),
            jb2aCheck: fn("() => boolean", "Check if JB2A is available"),
            simpleAttack: fn("(sourceToken, targetToken, animation, ...) => void", "Play simple attack animation"),
            preloadAnimations: fn("async (animations, showProgress?) => Promise", "Preload animation files")
          }
        },
        spellUtils: {
          type: "object",
          detail: "object",
          info: "Spell utility functions",
          children: {
            getClassSpells: fn("(identifier) => string[]", "Get spells for a class"),
            getAllClassSpells: fn("() => object", "Get all class spell lists"),
            isClassSpell: fn("(item, identifier) => boolean", "Check if spell belongs to class"),
            getCompendiumSpell: fn("async (name, ...) => Promise<Item>", "Get spell from compendium")
          }
        },
        crosshairUtils: {
          type: "object",
          detail: "object",
          info: "Crosshair utility functions",
          children: {
            aimCrosshair: fn("async (...) => Promise<object>", "Show aiming crosshair")
          }
        }
      };
      const CPR_COMPLETIONS = {
        utils: {
          type: "object",
          detail: "object",
          info: "CPR utility modules",
          children: CPR_UTILS
        },
        Summons: {
          type: "class",
          detail: "class",
          info: "CPR Summons handler",
          children: {
            spawn: fn("async (sourceActors, updates?, originItem, summonerToken, options?) => Promise", "Spawn summoned creatures")
          }
        },
        Teleport: {
          type: "class",
          detail: "class",
          info: "CPR Teleport handler"
        },
        Crosshairs: {
          type: "class",
          detail: "class",
          info: "CPR Crosshairs class"
        },
        DialogApp: {
          type: "class",
          detail: "class",
          info: "CPR Dialog application"
        },
        EmbeddedMacros: {
          type: "class",
          detail: "class",
          info: "CPR Embedded Macros application"
        },
        macros: prop("object", "CPR macro implementations"),
        legacyMacros: prop("object", "CPR legacy macro implementations"),
        integration: {
          type: "object",
          detail: "object",
          info: "Third-party integrations",
          children: {
            ddbi: fn("(item) => Promise<object>", "Get D&D Beyond Importer automation"),
            acc: prop("object", "Additional Compendiums Companion integration")
          }
        }
      };
      if (!tree.chrisPremades) {
        tree.chrisPremades = { type: "object", detail: "object", info: "chris-premades (CPR) module namespace" };
      }
      mergeCompletions(tree.chrisPremades, CPR_COMPLETIONS);
      console.log("midi-qol | Injected rich chrisPremades completions");
    }
    rebuildSignatureMap();
    console.log("midi-qol | Midi-qol completions injected successfully");
  });
}
__name(injectMidiCompletions, "injectMidiCompletions");

// src/module/apps/OverTimeEditor.ts
var { ApplicationV2: ApplicationV213, HandlebarsApplicationMixin: HandlebarsApplicationMixin14 } = foundry.applications.api;
var OverTimeEditor = class _OverTimeEditor extends HandlebarsApplicationMixin14(ApplicationV213) {
  static {
    __name(this, "OverTimeEditor");
  }
  constructor(editorOptions, appOptions) {
    super(appOptions);
    this.originalValue = editorOptions.value;
    this.effect = editorOptions.effect;
    this.parentDoc = editorOptions.parent;
    this.onSubmitCallback = editorOptions.onSubmit;
    this.onCancelCallback = editorOptions.onCancel;
    this.submitted = false;
    this.currentData = this.#buildFormData(this.originalValue);
    this.originalSerialized = this.#serializeCurrentData();
    this.originalPermanent = this.currentData.permanent;
  }
  static DEFAULT_OPTIONS = {
    id: "midi-qol-overtime-editor",
    tag: "form",
    window: {
      title: "midi-qol.OverTimeEditor.Title",
      resizable: true,
      contentClasses: ["standard-form"]
    },
    position: {
      width: 550,
      height: 700
    },
    form: {
      closeOnSubmit: false,
      submitOnChange: true,
      handler: _OverTimeEditor.#onFormChange
    },
    actions: {
      save: _OverTimeEditor.#onSave,
      cancel: _OverTimeEditor.#onCancel
    }
  };
  static PARTS = {
    form: {
      template: "modules/midi-qol/templates/apps/OverTimeEditor/overTimeEditor.hbs",
      scrollable: [".overtime-editor-scroll"]
    }
  };
  get title() {
    return `${i18n("midi-qol.OverTimeEditor.Title")}: ${this.effect?.name ?? ""}`;
  }
  /** Serialize currentData through the same serializer used for saving */
  #serializeCurrentData() {
    const data = { ...this.currentData };
    data.saveAbility = data.saveAbilityString;
    data.saveDC = data.saveDCRaw;
    return serializeOvertimeDetails(data);
  }
  get hasUnsavedChanges() {
    return this.#serializeCurrentData() !== this.originalSerialized;
  }
  async close(options) {
    if (!this.submitted && this.hasUnsavedChanges) {
      const confirmed = await foundry.applications.api.DialogV2.confirm({
        window: { title: i18n("midi-qol.OverTimeEditor.UnsavedTitle") },
        content: `<p>${i18n("midi-qol.OverTimeEditor.UnsavedContent")}</p>`,
        yes: { label: i18n("midi-qol.OverTimeEditor.CloseWithoutSaving") },
        no: { label: i18n("midi-qol.OverTimeEditor.Cancel") }
      });
      if (!confirmed) return this;
    }
    return super.close(options);
  }
  /**
   * Parse the raw OverTime string into key=value pairs without @-field evaluation.
   * We don't call parseOvertimeDetails because that evaluates @-expressions via Roll.safeEval,
   * which would lose the raw formula strings the user needs to edit.
   */
  parseRawOvertime(specString) {
    const raw = {};
    if (!specString) return raw;
    const cleanSpec = specString.replace(/\s*=\s*/g, "=").replace(/\s*,\s*/g, ",").replace("\n", "");
    let parts;
    if (cleanSpec.includes("#")) parts = cleanSpec.split("#");
    else parts = cleanSpec.split(",");
    for (const part of parts) {
      const p = part.split("=");
      if (p.length >= 2) {
        raw[p[0]] = p.slice(1).join("=");
      }
    }
    return raw;
  }
  /** Build the internal form data representation from a raw OverTime string */
  #buildFormData(specString) {
    const raw = this.parseRawOvertime(specString);
    const data = {
      turn: raw.turn || "start",
      label: (raw.name ?? raw.label ?? this.effect?.name ?? "").replace(/"/g, ""),
      rollType: raw.rollType ?? (raw.saveAbility ? "save" : "damage"),
      saveAbilityString: raw.saveAbility ?? "",
      saveDCRaw: raw.saveDC ?? "",
      saveMagic: parseBool(raw.saveMagic, false),
      damageRoll: raw.damageRoll ?? "",
      damageType: raw.damageType ?? "",
      damageBeforeSave: parseBool(raw.damageBeforeSave, false),
      saveDamage: raw.saveDamage ?? "nodamage",
      actionSave: raw.actionSave ?? "none",
      applyCondition: raw.applyCondition ?? raw.condition ?? "",
      removeCondition: raw.removeCondition ?? "",
      macro: raw.macro ?? "",
      itemName: raw.itemName ?? "",
      chatFlavor: raw.chatFlavor ?? "",
      rollMode: raw.rollMode ?? "default",
      fastForwardDamage: parseBool(raw.fastForwardDamage, true),
      fastForwardAttack: parseBool(raw.fastForwardAttack, true),
      autoRollAttack: parseBool(raw.autoRollAttack, true),
      autoRollDamage: parseBool(raw.autoRollDamage, true),
      allowIncapacitated: parseBool(raw.allowIncapacitated, false),
      killAnim: parseBool(raw.killAnim, false),
      // Count fields — defaults
      saveCountEnabled: false,
      saveCountNum: 1,
      saveCountMode: "remove",
      saveCountConsecutive: false,
      saveCountEffectSpec: "",
      failCountEnabled: false,
      failCountNum: 1,
      failCountMode: "permanent",
      failCountConsecutive: false,
      failCountEffectSpec: "",
      // Runtime state from effect flags (not part of spec string)
      permanent: false
    };
    if (raw.saveCount !== void 0) {
      data.saveCountEnabled = true;
      const parsed = this.parseCountFields(raw.saveCount);
      data.saveCountNum = parsed.count;
      data.saveCountMode = parsed.mode;
      data.saveCountConsecutive = parsed.consecutive;
      data.saveCountEffectSpec = parsed.effectSpec;
    } else if (raw.saveRemove !== void 0) {
      foundry.utils.logCompatibilityWarning(
        `midi-qol | OverTimeEditor | saveRemove is deprecated, use saveCount instead (saveRemove=${raw.saveRemove})`,
        { since: "midi-qol 13.0.43", until: "midi-qol 13.1.0" }
      );
      if (raw.saveRemove === "true" || raw.saveRemove === "1") {
        data.saveCountEnabled = true;
        data.saveCountNum = 1;
        data.saveCountMode = "remove";
      } else if (raw.saveRemove !== "false" && raw.saveRemove !== "0") {
        data.saveCountEnabled = true;
        const parsed = this.parseCountFields(raw.saveRemove);
        data.saveCountNum = parsed.count;
        data.saveCountMode = parsed.mode;
        data.saveCountConsecutive = parsed.consecutive;
        data.saveCountEffectSpec = parsed.effectSpec;
      }
    }
    if (raw.failCount !== void 0) {
      data.failCountEnabled = true;
      const parsed = this.parseCountFields(raw.failCount);
      data.failCountNum = parsed.count;
      data.failCountMode = parsed.mode;
      data.failCountConsecutive = parsed.consecutive;
      data.failCountEffectSpec = parsed.effectSpec;
    } else if (raw.failRemove !== void 0) {
      foundry.utils.logCompatibilityWarning(
        `midi-qol | OverTimeEditor | failRemove is deprecated, use failCount instead (failRemove=${raw.failRemove})`,
        { since: "midi-qol 13.0.43", until: "midi-qol 13.1.0" }
      );
      data.failCountEnabled = true;
      const parsed = this.parseCountFields(raw.failRemove);
      data.failCountNum = parsed.count;
      data.failCountMode = parsed.mode;
      data.failCountConsecutive = parsed.consecutive;
      data.failCountEffectSpec = parsed.effectSpec;
    }
    data.permanent = !!this.effect?.flags?.[MODULE_ID]?.overtime?.permanent;
    return data;
  }
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.details = this.currentData;
    context.isSaveType = ["save", "check", "skill"].includes(this.currentData.rollType);
    context.isSaveRoll = this.currentData.rollType === "save";
    context.isCheckRoll = this.currentData.rollType === "check";
    context.isSkillRoll = this.currentData.rollType === "skill";
    context.showSaveDamage = context.isSaveType && !this.currentData.damageBeforeSave;
    const selectedValues = this.currentData.saveAbilityString ? this.currentData.saveAbilityString.split("|").map((s) => s.trim().toLowerCase()) : [];
    context.selectedAbilities = selectedValues;
    context.selectedAbility = selectedValues[0] ?? "";
    const abilitySelectOptions = {};
    for (const [key, value] of Object.entries(CONFIG.DND5E?.abilities ?? {})) {
      abilitySelectOptions[key] = value.label ?? key;
    }
    context.abilitySelectOptions = abilitySelectOptions;
    const skillSelectOptions = {};
    for (const [key, value] of Object.entries(CONFIG.DND5E?.skills ?? {})) {
      skillSelectOptions[key] = value.label ?? key;
    }
    context.skillSelectOptions = skillSelectOptions;
    context.saveCountEnabled = this.currentData.saveCountEnabled;
    context.saveCountNum = this.currentData.saveCountNum;
    context.saveCountMode = this.currentData.saveCountMode;
    context.saveCountConsecutive = this.currentData.saveCountConsecutive;
    context.saveCountEffectSpec = this.currentData.saveCountEffectSpec;
    context.failCountEnabled = this.currentData.failCountEnabled;
    context.failCountNum = this.currentData.failCountNum;
    context.failCountMode = this.currentData.failCountMode;
    context.failCountConsecutive = this.currentData.failCountConsecutive;
    context.failCountEffectSpec = this.currentData.failCountEffectSpec;
    context.permanent = this.currentData.permanent;
    context.turnOptions = {
      start: "midi-qol.OverTimeEditor.TurnStart",
      end: "midi-qol.OverTimeEditor.TurnEnd"
    };
    context.rollTypeOptions = {
      save: "midi-qol.OverTimeEditor.RollTypeSave",
      check: "midi-qol.OverTimeEditor.RollTypeCheck",
      skill: "midi-qol.OverTimeEditor.RollTypeSkill",
      damage: "midi-qol.OverTimeEditor.RollTypeDamage"
    };
    const damageTypeOptions = { "": "None" };
    const damageTypes = CONFIG.DND5E?.damageTypes ?? {};
    for (const [key, value] of Object.entries(damageTypes)) {
      damageTypeOptions[key] = value.label ?? key;
    }
    const healingTypes = CONFIG.DND5E?.healingTypes ?? {};
    for (const [key, value] of Object.entries(healingTypes)) {
      damageTypeOptions[key] = value.label ?? key;
    }
    if (GameSystemConfig.damageTypes?.["vitality"]) {
      damageTypeOptions["vitality"] = GameSystemConfig.damageTypes["vitality"].label ?? "Vitality";
    }
    context.damageTypeOptions = damageTypeOptions;
    context.saveDamageOptions = {
      nodamage: "midi-qol.OverTimeEditor.NoDamage",
      halfdamage: "midi-qol.OverTimeEditor.HalfDamage",
      fulldamage: "midi-qol.OverTimeEditor.FullDamage"
    };
    context.countModeOptions = {
      remove: "midi-qol.OverTimeEditor.CountRemove",
      permanent: "midi-qol.OverTimeEditor.CountPermanent"
    };
    context.actionSaveOptions = {
      none: "midi-qol.OverTimeEditor.ActionSaveNone",
      dialog: "midi-qol.OverTimeEditor.ActionSaveDialog",
      roll: "midi-qol.OverTimeEditor.ActionSaveRoll"
    };
    context.rollModeOptions = {
      default: "midi-qol.OverTimeEditor.RollModeDefault",
      publicroll: "midi-qol.OverTimeEditor.RollModePublic",
      gmroll: "midi-qol.OverTimeEditor.RollModeGM",
      blindroll: "midi-qol.OverTimeEditor.RollModeBlind",
      selfroll: "midi-qol.OverTimeEditor.RollModeSelf"
    };
    return context;
  }
  /** Parse a count action string into its component fields for the form */
  parseCountFields(value) {
    let consecutive = false;
    let working = value;
    const digitEnd = working.search(/\D/);
    if (digitEnd > 0 && working[digitEnd] === "c") {
      consecutive = true;
      working = working.substring(0, digitEnd) + working.substring(digitEnd + 1);
    }
    const minusIndex = working.indexOf("-");
    const plusIndex = working.indexOf("+");
    let actionIndex = -1;
    let mode = "remove";
    if (minusIndex > 0 && (plusIndex === -1 || minusIndex < plusIndex)) {
      actionIndex = minusIndex;
      mode = "remove";
    } else if (plusIndex > 0) {
      actionIndex = plusIndex;
      mode = "permanent";
    }
    if (actionIndex === -1) {
      return { count: Number(working) || 1, mode: "remove", consecutive, effectSpec: "" };
    }
    const count = Number(working.substring(0, actionIndex)) || 1;
    const effectSpec = working.substring(actionIndex + 1) || "";
    return { count, mode, consecutive, effectSpec };
  }
  async _onRender(context, options) {
    await super._onRender(context, options);
    new foundry.applications.ux.DragDrop.implementation({
      dropSelector: "input[name='itemName'], input[name='macro']",
      callbacks: {
        drop: this.#onDrop.bind(this)
      }
    }).bind(this.element);
  }
  async #onDrop(ev) {
    ev.preventDefault();
    const data = foundry.applications.ux.TextEditor.getDragEventData(ev);
    if (data.uuid && ev.target) {
      ev.target.value = data.uuid;
    }
  }
  /** Handle form changes (submitOnChange) — updates internal state and re-renders */
  static async #onFormChange(event, form, formData) {
    const data = foundry.utils.expandObject(formData.object);
    if ("saveAbility" in data) {
      const val = data.saveAbility;
      if (Array.isArray(val)) {
        this.currentData.saveAbilityString = val.join("|");
      } else {
        this.currentData.saveAbilityString = val ?? "";
      }
    }
    if ("saveDC" in data) {
      this.currentData.saveDCRaw = data.saveDC;
    }
    const directFields = [
      "turn",
      "label",
      "rollType",
      "saveMagic",
      "damageRoll",
      "damageType",
      "damageBeforeSave",
      "saveDamage",
      "actionSave",
      "applyCondition",
      "removeCondition",
      "macro",
      "itemName",
      "chatFlavor",
      "rollMode",
      "fastForwardDamage",
      "fastForwardAttack",
      "autoRollAttack",
      "autoRollDamage",
      "allowIncapacitated",
      "killAnim",
      "saveCountEnabled",
      "saveCountNum",
      "saveCountMode",
      "saveCountConsecutive",
      "saveCountEffectSpec",
      "failCountEnabled",
      "failCountNum",
      "failCountMode",
      "failCountConsecutive",
      "failCountEffectSpec",
      "permanent"
    ];
    for (const field of directFields) {
      if (field in data) {
        this.currentData[field] = data[field];
      }
    }
    this.render();
  }
  /** Save action — serialize current data and call the callback */
  static async #onSave() {
    const data = { ...this.currentData };
    data.saveAbility = data.saveAbilityString;
    data.saveDC = data.saveDCRaw;
    const result = serializeOvertimeDetails(data);
    if (this.currentData.permanent !== this.originalPermanent) {
      const flagUpdate = {
        [`flags.${MODULE_ID}.overtime.permanent`]: this.currentData.permanent || null
      };
      await this.effect.update(flagUpdate);
    }
    this.submitted = true;
    this.onSubmitCallback(result);
    this.close();
  }
  static #onCancel() {
    this.submitted = true;
    this.onCancelCallback();
    this.close();
  }
  async _preClose(options) {
    await super._preClose(options);
    if (!this.submitted) {
      this.onCancelCallback();
    }
  }
};

// src/module/ChatClasses/ChatMessageMidi.ts
var midiAutoAppliesDamage = /* @__PURE__ */ __name(() => !!configSettings.autoApplyDamage?.toLowerCase().includes("yes"), "midiAutoAppliesDamage");
var removeAll = /* @__PURE__ */ __name((html, sel) => html.querySelectorAll(sel).forEach((el) => el.remove()), "removeAll");
var hideAll = /* @__PURE__ */ __name((html, sel) => html.querySelectorAll(sel).forEach((el) => el.style.display = "none"), "hideAll");
var stripHighlight = /* @__PURE__ */ __name((html, sel) => html.querySelectorAll(sel).forEach(
  (el) => el.classList.remove("success", "failure", "critical", "fumble")
), "stripHighlight");
var _highlighted = null;
var onTargetHover = /* @__PURE__ */ __name((event) => {
  event.preventDefault();
  if (!canvas.scene?.active) return;
  const token = canvas.tokens?.get(event.currentTarget?.dataset?.id ?? "");
  if (token?.isVisible) {
    if (!token.controlled) token._onHoverIn(event);
    _highlighted = token;
  }
}, "onTargetHover");
var onTargetHoverOut = /* @__PURE__ */ __name((event) => {
  event.preventDefault();
  if (!canvas.scene?.active) return;
  if (_highlighted) _highlighted._onHoverOut(event);
  _highlighted = null;
}, "onTargetHoverOut");
var onTargetSelect = /* @__PURE__ */ __name((event) => {
  event.stopPropagation();
  event.preventDefault();
  if (!canvas.scene?.active) return;
  const token = canvas.tokens?.get(event.currentTarget.dataset.id);
  if (token?.controlled) token?.release();
  else if (token && token?.isVisible && game.user && token.actor?.testUserPermission(game.user, "OWNER")) {
    token.control({ releaseOthers: false });
    canvas.animatePan(token.center);
  }
}, "onTargetSelect");
function onTargetShow(event) {
  event.stopImmediatePropagation();
  event.preventDefault();
  if (!canvas.scene?.active) return;
  const token = canvas.tokens?.get(event.currentTarget.dataset.id);
  if (game.user && token?.actor?.testUserPermission(game.user, "OWNER")) {
    token.actor.sheet?.render(true);
  }
}
__name(onTargetShow, "onTargetShow");
var ChatMessageMidi;
function setupChatMessageMidi() {
  ChatMessageMidi = defineChatMessageMidiClass(globalThis.dnd5e.documents.ChatMessage5e);
  CONFIG.ChatMessage.batchSize = 25;
  globalThis.dnd5e.documents.ChatMessage5e.TRAY_TYPES.push("midi-damage-application");
}
__name(setupChatMessageMidi, "setupChatMessageMidi");
function defineChatMessageMidiClass(baseClass) {
  return class ChatMessageMidi extends baseClass {
    static {
      __name(this, "ChatMessageMidi");
    }
    prepareDerivedData() {
      super.prepareDerivedData();
      const midiFlags2 = this.flags?.[MODULE_ID];
      if (!midiFlags2) return;
      const attackData = midiFlags2.attackRoll;
      if (attackData && !(attackData instanceof Roll)) {
        midiFlags2.attackRoll = Roll.fromData(attackData);
      }
      for (const field of [
        "damageRolls",
        "bonusDamageRolls",
        "otherDamageRolls",
        "utilityRolls"
      ]) {
        const data = midiFlags2[field];
        if (data?.length) {
          midiFlags2[field] = data.map((r) => r instanceof Roll ? r : Roll.fromData(r));
        }
      }
    }
    static get TRAY_TYPES() {
      return globalThis.dnd5e.documents.ChatMessage5e.TRAY_TYPES.concat(["midi-damage-application"]);
    }
    get isRoll() {
      if (this.flags?.[MODULE_ID]?.messageType) return false;
      return super.isRoll;
    }
    get hasRolls() {
      if ((this.flags?.[MODULE_ID]?.roll?.length ?? 0) > 0) return true;
      return super.isRoll;
    }
    // midi has it's own target handling so don't display the attack targets here (however, DO display the mastery)
    _enrichAttackTargets(html) {
      const attackRoll = this.rolls[0];
      if (!(attackRoll instanceof dnd5e.dice.D20Roll)) return;
      const masteryConfig = CONFIG.DND5E.weaponMasteries[attackRoll.options.mastery];
      if (!masteryConfig) return;
      const p = document.createElement("p");
      p.classList.add("supplement");
      let mastery = masteryConfig.label;
      if (masteryConfig.reference) mastery = `
        <a class="content-link" draggable="true" data-link data-uuid="${masteryConfig.reference}"
           data-tooltip="${mastery}" style="text-wrap: auto;">${mastery}</a>
      `;
      p.innerHTML = `<string>${i18n("DND5E.WEAPON.Mastery.Flavor")}</string> ${mastery}`;
      const condensedSection = html.querySelector(".midi-results > .flexrow");
      if (condensedSection) {
        condensedSection.after(p);
      } else {
        html.querySelector(".midi-qol-attack-roll")?.appendChild(p);
      }
    }
    get canSelectTargets() {
      if (this.flags?.[MODULE_ID]?.messageType === "attack") return true;
      return super.canSelectTargets;
    }
    get canApplyDamage() {
      if ((this.flags?.[MODULE_ID]?.damageDetail?.length ?? 0) > 0 && this.isContentVisible && !!canvas.tokens?.controlled.length) return true;
      return super.canApplyDamage;
    }
    // Patch for getAssociatedItem not preparing data on items recovered from item.data
    getAssociatedItem() {
      if (this.flags?.[MODULE_ID]?.syntheticItem) {
        const storedData = this.flags.dnd5e?.item?.data;
        const actor = super.getAssociatedActor();
        if (storedData) return new Item.implementation(storedData, { parent: actor });
      }
      return super.getAssociatedItem();
    }
    /**
    * Select the hit or missed targets.
    * @param {HTMLElement} li    The chat entry which contains the roll data.
    * @param {string} type       The type of selection ('hit' or 'miss').
    */
    selectTargets(li, type) {
      if (!canvas?.ready) return;
      if (!this.flags?.[MODULE_ID]) return super.selectTargets(li, type);
      const targetData = this.getFlag(MODULE_ID, "targets") || [];
      const targetUuids = targetData.map((t) => t.uuid);
      let hitTargetUuids = this.getFlag(MODULE_ID, "hitTargetUuids") || [];
      let uuids;
      if (type === "hit") uuids = hitTargetUuids;
      else uuids = targetUuids.filter((uuid) => !hitTargetUuids.includes(uuid));
      canvas.tokens?.releaseAll();
      uuids.forEach((uuid) => {
        const token = getToken(uuid);
        if (!token?.actor || !game.user) return;
        if (token.isVisible && token.actor.testUserPermission(game.user, "OWNER")) {
          token.control({ releaseOthers: false });
        }
      });
    }
    /**
    * Apply rolled dice damage to the token or tokens which are currently controlled.
    * This allows for damage to be scaled by a multiplier to account for healing, critical hits, or resistance
    *
    * @param {HTMLElement} li      The chat entry which contains the roll data
    * @param {number} multiplier   A damage multiplier to apply to the rolled damage.
    * @returns {Promise}
    */
    applyChatCardDamage(li, multiplier) {
      const type = this.flags.dnd5e?.roll?.type;
      if (type !== void 0 || !this.flags?.[MODULE_ID]) return super.applyChatCardDamage(li, multiplier);
      const rollsToCheck = this.rolls.filter((r) => MQDamageRollTypes.includes(foundry.utils.getProperty(r, "options.midi-qol.rollType")));
      const damages = game.system.dice.aggregateDamageRolls(rollsToCheck, { respectProperties: true }).map((roll) => ({
        value: roll.total,
        type: roll.options.type,
        properties: new Set(roll.options.properties ?? [])
      }));
      if (canvas.tokens) {
        return Promise.all(canvas.tokens.controlled.map((t) => {
          return t.actor?.applyDamage(damages, { multiplier, invertHealing: false, ignore: true });
        }));
      }
    }
    applyChatCardTemp(li) {
      if (!canvas.tokens) return;
      const rollsToCheck = this.rolls.filter((r) => MQDamageRollTypes.includes(foundry.utils.getProperty(r, "options.midi-qol.rollType")));
      const total = rollsToCheck.reduce((acc, roll) => acc + (roll.total ?? 0), 0);
      return Promise.all(canvas.tokens.controlled.map((t) => {
        return t.actor?.applyTempHP(total);
      }));
    }
    collectRolls(rollsToAccumulate, multiRolls = false, options) {
      const returns = [];
      setDamageRollMinTerms(rollsToAccumulate);
      const formulaInToolTip = ["formula", "formulaadv"].includes(configSettings.rollAlternate);
      const hideDetails = this.author?.isGM && !game.user?.isGM && (configSettings.gmDamageDisplay ?? "full") !== "full";
      const hideFormula = hideDetails || options.blind && !game.user?.isGM;
      const rollSets = multiRolls ? rollsToAccumulate.map((r) => [r]) : [rollsToAccumulate];
      for (const rolls of rollSets) {
        let { formula, total, breakdown } = game.system.dice.aggregateDamageRolls(rolls).reduce((obj, r) => {
          obj.formula.push(r.formula);
          obj.total += Math.max(0, r.total);
          obj.breakdown.push(this._simplifyDamageRoll(r));
          return obj;
        }, { formula: [], total: 0, breakdown: [] });
        formula = formula.join(" ");
        formula = formula.replace(/^\s+\+\s+/, "");
        formula = formula.replaceAll(/  /g, " ");
        if (multiRolls) {
          foundry.utils.setProperty(rolls[0], "flags.midi-qol.breakdown", breakdown);
          foundry.utils.setProperty(rolls[0], "flags.midi-qol.total", total);
        }
        if (hideDetails && (configSettings.gmDamageDisplay ?? "full") === "hidden") {
          total = "--";
        }
        if (options.blind && !game.user?.isGM) total = "--";
        const roll = document.createElement("div");
        roll.classList.add("dice-roll");
        let tooltipContents = "";
        if (!hideDetails) tooltipContents = breakdown.reduce((str, { type, total: total2, constant, dice }) => {
          const config = GameSystemConfig.damageTypes[type] ?? GameSystemConfig.healingTypes[type];
          return `${str}
              <section class="tooltip-part">
                <div class="dice">
                  <ol class="dice-rolls">
                    ${dice.reduce((str2, { result, classes }) => `
                      ${str2}<li class="roll ${classes}">${result}</li>
                    `, "")}
                    ${constant ? `
                    <li class="constant"><span class="sign">${constant < 0 ? "-" : "+"}</span>${Math.abs(constant)}</li>
                    ` : ""}
                  </ol>
                  <div class="total">
                    ${config ? `<img src="${config.icon}" alt="${config.label}">` : ""}
                    <span class="label">${config?.label ?? ""}</span>
                    <span class="value">${total2}</span>
                  </div>
                </div>
              </section>
            `;
        }, "");
        let diceFormula = "";
        if (!hideFormula) diceFormula = `<div class="dice-formula">${formula}</div>`;
        roll.innerHTML = `
      <div class="dice-result">
      ${formulaInToolTip ? "" : diceFormula}
        <div class="dice-tooltip-collapser">
          <div class="dice-tooltip">
            ${formulaInToolTip ? diceFormula : ""}
            ${tooltipContents}
          </div>
        </div>
        <h4 class="dice-total">${total}${this._buildDamageTypeIcons(breakdown)}</h4>
      </div>
    `;
        returns.push(roll);
      }
      return returns;
    }
    _buildDamageTypeIcons(breakdown) {
      const uniqueTypes = [...new Set(breakdown.map((b) => b.type).filter(Boolean))];
      return uniqueTypes.map((type) => {
        const config = GameSystemConfig.damageTypes[type] ?? GameSystemConfig.healingTypes?.[type];
        if (!config?.icon) return "";
        return `<dnd5e-icon class="midi-damage-type-icon" src="${config.icon}" aria-label="${config.label ?? ""}" data-tooltip="${config.label ?? ""}"></dnd5e-icon>`;
      }).join("");
    }
    // Removes the dnd5e effects tray from the rendered chat card when midi's
    // autoItemEffects suppresses the apply button. dnd5e v5.x emits an
    // <effect-application> custom element from usage-card.hbs which renders
    // itself in connectedCallback — there's no longer a _enrichUsageEffects
    // method to skip, so we strip the element from the DOM after super runs.
    _maybeStripEffectsTray(html) {
      if (["applyNoButton", "applyRemove"].includes(configSettings.autoItemEffects)) {
        html.querySelectorAll("effect-application").forEach((el) => el.remove());
      }
    }
    _enrichDamageTooltip(rolls, html) {
      if (this.flags?.dnd5e?.roll?.type !== void 0 || !this.flags?.[MODULE_ID])
        return super._enrichDamageTooltip(rolls, html);
      for (let rollType of MQDamageRollTypes) {
        const rollsToCheck = this.rolls.filter((r) => foundry.utils.getProperty(r, "options.midi-qol.rollType") === rollType);
        let rType = "damage";
        if (rollType === "otherDamage") rType = "other-damage";
        else if (rollType === "bonusDamage") rType = "bonus-damage";
        if (rollsToCheck?.length) {
          html.querySelectorAll(`.midi-${rType}-roll`)?.forEach((el) => el.remove());
          for (let roll of this.collectRolls(rollsToCheck, configSettings.mergeCardMultiDamage, { blind: this.blind })) {
            roll.classList.add(`midi-${rType}-roll`);
            if (rType === "bonus-damage") {
              const flavor = document.createElement("div");
              const flavors = rollsToCheck.map((r) => r.options.flavor ?? r.options.type);
              const bonusDamageFlavor = flavors.join(", ");
              flavor.classList.add("midi-bonus-damage-flavor");
              flavor.innerHTML = bonusDamageFlavor;
              html.querySelector(`.midi-qol-${rType}-roll`)?.appendChild(flavor);
            }
            html.querySelector(`.midi-qol-${rType}-roll`)?.appendChild(roll);
          }
        }
      }
      let shouldAddButtons = configSettings.addChatDamageButtons === "both" || configSettings.addChatDamageButtons === "gm" && game.user?.isGM || configSettings.addChatDamageButtons === "pc" && !game.user?.isGM;
      if (game.user?.isGM && configSettings.autoApplyDamage === "none") shouldAddButtons = true;
      if (shouldAddButtons) {
        for (let dType of MQDamageRollTypes) {
          rolls = this.rolls.filter((r) => foundry.utils.getProperty(r, "options.midi-qol.rollType") === dType);
          if (!rolls.length) continue;
          let damageApplication = document.createElement("midi-damage-application");
          damageApplication.damageType = dType;
          damageApplication.damages = game.system.dice.aggregateDamageRolls(rolls, { respectProperties: true }).map((roll) => ({
            value: Math.max(0, roll.total),
            type: roll.options.type,
            properties: new Set(roll.options.properties ?? [])
          }));
          damageApplication.toggleAttribute("open", !midiAutoAppliesDamage());
          html.querySelector(".message-content")?.appendChild(damageApplication);
        }
      }
    }
    _collapseTrays(html) {
      super._collapseTrays(html);
      if (!this.flags?.[MODULE_ID]?.messageType) return;
      const open = !midiAutoAppliesDamage();
      html.querySelectorAll("midi-damage-application").forEach((el) => {
        el.toggleAttribute("open", open);
        el.querySelector(".collapsible")?.classList.toggle("collapsed", !open);
      });
    }
    _highlightCriticalSuccessFailure(html) {
      super._highlightCriticalSuccessFailure(html);
      const totals = html.querySelectorAll(".dice-total");
      for (let [index, d20Roll] of this.rolls.entries()) {
        const total = totals[index];
        if (!total || total.classList.contains("critical") || total.classList.contains("fumble")) continue;
        const d0 = d20Roll?.dice?.[0];
        if (!d0 || d0.faces !== 20 || d0.values?.length !== 1) continue;
        const d20 = dnd5e.dice.D20Roll.fromRoll(d20Roll);
        if (d20.isCritical) {
          total.classList.add("critical");
          if (!total.querySelector(".icons")) {
            const icons = document.createElement("div");
            icons.classList.add("icons");
            const icon = document.createElement("i");
            icon.classList.add("fas", "fa-check-double");
            icon.setAttribute("inert", "");
            icons.append(icon);
            total.append(icons);
          }
        } else if (d20.isFumble) {
          total.classList.add("fumble");
          if (!total.querySelector(".icons")) {
            const icons = document.createElement("div");
            icons.classList.add("icons");
            for (let i = 0; i < 2; i++) {
              const icon = document.createElement("i");
              icon.classList.add("fas", "fa-xmark");
              icon.setAttribute("inert", "");
              icons.append(icon);
            }
            total.append(icons);
          }
        }
      }
      this._applyMidiHighlightSettings(html);
    }
    /**
     * Strip or adjust classes and icons based on midi highlight/visibility settings.
     */
    _applyMidiHighlightSettings(html) {
      const isMidiMessage = !!this.flags?.[MODULE_ID]?.messageType;
      const hideResult = isMidiMessage && !game.user?.isGM && (["whisper", "gmOnly"].includes(configSettings.autoCheckHit) || safeGetGameSetting("dnd5e", "attackRollVisibility") === "none");
      const totals = html.querySelectorAll(".dice-total");
      for (const total of Array.from(totals)) {
        if (hideResult || !configSettings.highlightSuccess) {
          total.classList.remove("success", "failure", "critical", "fumble");
          total.querySelector(".icons")?.remove();
        } else if (configSettings.highLightCriticalAttackOnly) {
          const hadSuccessOrFailure = total.classList.contains("success") || total.classList.contains("failure");
          total.classList.remove("success", "failure");
          if (hadSuccessOrFailure && !total.classList.contains("critical") && !total.classList.contains("fumble")) {
            total.querySelector(".icons")?.remove();
          }
        }
      }
    }
    /**
     * Add critical/fumble/success/failure classes and icons for midi messages.
     * Used by _enrichChatCard for usage-type messages where _highlightCriticalSuccessFailure
     * is not called by Foundry. Does not mutate document flags (safe during async super._enrichChatCard).
     */
    _highlightMidiRolls(html) {
      function makeIcon(cls) {
        const icon = document.createElement("i");
        icon.classList.add("fas", cls);
        icon.setAttribute("inert", "");
        return icon;
      }
      __name(makeIcon, "makeIcon");
      const totals = html.querySelectorAll(".dice-total");
      for (let [index, d20Roll] of this.rolls.entries()) {
        const total = totals[index];
        if (!total) continue;
        total.classList.remove("success", "failure", "critical", "fumble");
        total.querySelector(".icons")?.remove();
        const d0 = d20Roll?.dice?.[0];
        if (!d0 || d0.faces !== 20 || d0.values?.length !== 1) continue;
        const d20 = dnd5e.dice.D20Roll.fromRoll(d20Roll);
        if (d20.isCritical) total.classList.add("critical");
        if (d20.isFumble) total.classList.add("fumble");
        if (d20.dice[0]?.options?.target) {
          if (d20.isSuccess) total.classList.add("success");
          else total.classList.add("failure");
        }
        const icons = document.createElement("div");
        icons.classList.add("icons");
        if (total.classList.contains("critical")) icons.append(makeIcon("fa-check-double"));
        else if (total.classList.contains("fumble")) icons.append(makeIcon("fa-xmark"), makeIcon("fa-xmark"));
        else if (total.classList.contains("success")) icons.append(makeIcon("fa-check"));
        else if (total.classList.contains("failure")) icons.append(makeIcon("fa-xmark"));
        if (icons.children.length) total.append(icons);
      }
    }
    maskAttackRolls(html) {
      if (!this.author?.isGM || game.user?.isGM) return;
      const hitFlag = this.flags?.[MODULE_ID]?.isHit;
      const hitString = hitFlag === void 0 ? "" : hitFlag ? i18n("midi-qol.hits") : i18n("midi-qol.misses");
      const isCritical = this.flags?.[MODULE_ID]?.isCritical;
      const isFumble = this.flags?.[MODULE_ID]?.isFumble;
      const attackDisplay = configSettings.gmAttackDisplay ?? "full";
      const damageDisplay = configSettings.gmDamageDisplay ?? "full";
      let attackRollText;
      let hideCritFumble = false;
      switch (attackDisplay) {
        case "full":
          break;
        case "hideFormula":
          break;
        case "d20Only":
          attackRollText = `(d20) ${this.rolls[0]?.terms[0].total ?? "--"}`;
          break;
        case "attackTotal":
          attackRollText = `${this.rolls[0]?.total ?? "--"}`;
          break;
        case "hitMiss":
          hideCritFumble = true;
          attackRollText = hitString;
          break;
        case "hitMissCrit":
          attackRollText = hitString;
          if (isCritical) attackRollText = i18n("midi-qol.criticalHit");
          else if (isFumble) attackRollText = i18n("midi-qol.fumble");
          break;
        case "hidden":
          hideCritFumble = true;
          attackRollText = "--";
          break;
      }
      if (hideCritFumble) {
        html.querySelectorAll(".midi-qol-attack-roll .dice-total")?.forEach((el) => el.classList.remove("critical", "fumble"));
      }
      if (attackRollText) html.querySelectorAll(".midi-attack-roll .dice-total")?.forEach((el) => el.innerHTML = attackRollText);
      if (attackDisplay !== "full") {
        if (attackDisplay !== "hideFormula") {
          removeAll(html, ".midi-attack-roll .dice-formula");
        }
        removeAll(html, ".midi-attack-roll .dice-tooltip");
        html.querySelectorAll(".midi-attack-roll .dice-roll").forEach((el) => el.addEventListener("click", this.noDiceClicks.bind(this)));
        removeAll(html, ".midi-attribution-info");
      }
      if (damageDisplay === "totalOnly") {
        removeAll(html, ".midi-damage-roll .dice-formula");
        removeAll(html, ".midi-damage-roll .dice-tooltip");
        html.querySelectorAll(".midi-damage-roll .dice-roll").forEach((el) => el.addEventListener("click", this.noDiceClicks.bind(this)));
      } else if (damageDisplay === "hidden") {
        removeAll(html, ".midi-damage-roll .dice-formula");
        removeAll(html, ".midi-damage-roll .dice-tooltip");
        html.querySelectorAll(".midi-damage-roll .dice-roll").forEach((el) => el.addEventListener("click", this.noDiceClicks.bind(this)));
        html.querySelectorAll(".midi-damage-roll .dice-total")?.forEach((el) => el.innerHTML = "--");
      }
    }
    applyChatVisibility(html) {
      if (html.style.display === "none") return;
      const messageActor = fromUuidSync(this.flags?.[MODULE_ID]?.["sourceActorUuid"]) ?? this.speakerActor;
      const messageActorOwner = messageActor?.isOwner;
      if (foundry.utils.getProperty(this, "flags.midi-qol.type") === MESSAGE_TYPES.SAVES) {
        html.querySelectorAll('[data-action="rollSave"]').forEach((el) => el.disabled = false);
        html.querySelectorAll('[data-action="rollCheck"]').forEach((el) => el.disabled = false);
      }
      if (!this.isAuthor && !(messageActorOwner && saveToChatCard)) {
        hideAll(html, ".midi-qol-attack-buttons");
        hideAll(html, ".midi-qol-damage-buttons");
        hideAll(html, ".midi-qol-otherDamage-button");
      }
      html.querySelectorAll(".midi-qol-target-select").forEach((el) => {
        el.addEventListener("mouseenter", onTargetHover);
        el.addEventListener("mouseleave", onTargetHoverOut);
        el.addEventListener("click", onTargetSelect);
        el.addEventListener("contextmenu", onTargetShow);
      });
      if (game.user?.isGM) {
        removeAll(html, ".midi-qol-playerTokenName");
        if (configSettings.hidePlayerDamageCard && foundry.utils.getProperty(this, "flags.midi-qol.playerDamageCard")) {
          html.style.display = "none";
        }
        html.querySelectorAll(".midi-qol-hits-display").forEach((el) => el.style.display = "");
        hideAll(html, ".midi-qol-target-npc-Player");
        if (!configSettings.highlightSuccess) {
          stripHighlight(html, ".midi-qol-hits-display .midi-qol-hit-class");
          stripHighlight(html, ".midi-qol-saves-display .midi-qol-save-class");
        }
      } else {
        removeAll(html, ".midi-qol-gmTokenName");
        if (configSettings.autoCheckHit === "gmOnly") {
          hideAll(html, ".midi-qol-hits-display");
        }
        this.applySaveVisibility(html, messageActor);
        this.applyAttackVisibility(html);
        if (!this.isAuthor || configSettings.confirmAttackDamage === "gmOnly") {
          hideAll(html, ".midi-qol-confirm-damage-roll-complete-hit");
          hideAll(html, ".midi-qol-confirm-damage-roll-complete-miss");
          hideAll(html, ".midi-qol-confirm-damage-roll-complete-critical");
          hideAll(html, ".midi-qol-confirm-damage-roll-cancel");
        }
        const attackHidden = (configSettings.gmAttackDisplay ?? "full") !== "full";
        const damageHidden = (configSettings.gmDamageDisplay ?? "full") !== "full";
        if (this.author?.isGM && (attackHidden || damageHidden)) {
          const noDice = this.noDiceClicks.bind(this);
          if (attackHidden) {
            html.querySelectorAll(".midi-attack-roll .dice-roll").forEach((el) => el.addEventListener("click", noDice));
            html.querySelectorAll(".midi-attack-roll .dice-tooltip").forEach((el) => el.style.height = "0");
          }
          if (damageHidden) {
            html.querySelectorAll(".midi-damage-roll .dice-roll").forEach((el) => el.addEventListener("click", noDice));
            html.querySelectorAll(".midi-damage-roll .dice-tooltip").forEach((el) => el.style.height = "0");
          }
        }
      }
    }
    applySaveVisibility(html, messageActor) {
      const shouldDisplayChallenge = safeGetGameSetting("dnd5e", "challengeVisibility");
      const autoCheckSaves = configSettings.autoCheckSaves;
      if (autoCheckSaves === "gmOnly") {
        hideAll(html, ".midi-qol-saves-display");
        return;
      }
      if (!["all", "allShow"].includes(autoCheckSaves)) {
        html.querySelectorAll(".midi-qol-target-name[data-tooltip-html]")?.forEach((el) => el.removeAttribute("data-tooltip-html"));
        removeAll(html, ".midi-save-attribution-info");
      }
      const dcHidden = shouldDisplayChallenge === "player" && !messageActor?.hasPlayerOwner;
      const displayNPCSaves = shouldDisplayChallenge === "all" || shouldDisplayChallenge === "player" && !dcHidden;
      const displayPlayerSaves = shouldDisplayChallenge !== "none";
      if (!displayPlayerSaves || autoCheckSaves === "whisper" || this.blind) {
        removeAll(html, ".midi-qol-save-total");
        removeAll(html, ".midi-qol-save-tooltip");
        removeAll(html, ".midi-qol-save-symbol");
        removeAll(html, ".midi-qol-saveDC");
      }
      if (!displayNPCSaves || autoCheckSaves === "whisper" || this.blind) {
        if (!dcHidden) removeAll(html, ".midi-qol-npc-save-symbol");
        removeAll(html, ".midi-qol-npc-save-tooltip");
        removeAll(html, ".midi-qol-npc-save-total");
        if (!displayNPCSaves && !messageActor?.hasPlayerOwner) {
          removeAll(html, ".midi-qol-saveDC");
        }
      } else if (autoCheckSaves !== "allShow") {
        removeAll(html, ".midi-qol-npc-save-tooltip");
        if (autoCheckSaves === "allNoRoll") removeAll(html, ".midi-qol-npc-save-total");
      }
      if (!configSettings.highlightSuccess || !["all", "player"].includes(shouldDisplayChallenge) || ["whisper", "none"].includes(autoCheckSaves)) {
        stripHighlight(html, ".midi-qol-saves-display .midi-qol-save-class");
      }
      if (!displayNPCSaves && !dcHidden || !configSettings.highlightSuccess) {
        stripHighlight(html, ".midi-qol-save-class.midi-qol-npc-target");
      }
    }
    applyAttackVisibility(html) {
      if (!configSettings.optionalRules.ActiveDefenceDC) {
        removeAll(html, ".midi-qol-active-defence-dc");
      }
      const attackVisibility = safeGetGameSetting("dnd5e", "attackRollVisibility");
      const midiAttackVisibility = configSettings.autoCheckHit;
      if (!configSettings.highlightSuccess || attackVisibility === "none" || midiAttackVisibility === "whisper" || this.blind) {
        stripHighlight(html, ".midi-qol-hits-display .midi-qol-hit-class");
      }
      if (attackVisibility === "none" || midiAttackVisibility !== "all" || this.blind) {
        removeAll(html, ".midi-attack-roll .dice-total .icons");
        removeAll(html, ".midi-qol-hits-display .midi-qol-hit-symbol");
        removeAll(html, ".midi-qol-hit-symbol");
        removeAll(html, ".midi-qol-npc-ac");
      } else if (attackVisibility === "hideAC") {
        removeAll(html, ".midi-qol-npc-ac");
      }
    }
    _enrichChatCard(html) {
      if (!this.flags?.[MODULE_ID]?.messageType) {
        this.applyChatVisibility(html);
        super._enrichChatCard(html);
        this._maybeStripEffectsTray(html);
        this._addMarkerClasses(html);
        return;
      }
      if (debugEnabled > 1) log("Enriching chat card", Date.now(), this.id);
      this.maskAttackRolls(html);
      super._enrichChatCard(html);
      this._maybeStripEffectsTray(html);
      this.applyChatVisibility(html);
      if (this.flags?.[MODULE_ID]?.messageType) {
        this._highlightMidiRolls(html);
      }
      this._applyMidiHighlightSettings(html);
      this._addMarkerClasses(html);
      if (debugEnabled > 1) log("Finished enriching chat card", Date.now(), this.id);
    }
    /** Add CSS marker classes to avoid expensive :has() selectors during layout */
    _addMarkerClasses(html) {
      if (html.querySelector(".midi-qol-tooltiptext")) {
        html.querySelector(".message-content")?.classList.add("midi-qol-has-tooltip");
      }
      if (html.querySelector(".xmidi-qol-flex-container")) {
        html.classList.add("midi-qol-dmg-app-msg");
      }
      if (html.querySelector(".midi-qol-player-damage-card")) {
        html.classList.add("midi-qol-player-dmg-msg");
      }
    }
    noDiceClicks(event) {
      event.stopImmediatePropagation();
      return;
    }
  };
}
__name(defineChatMessageMidiClass, "defineChatMessageMidiClass");

// src/module/activities/SaveActivity.ts
var MidiSaveActivity;
var MidiSaveSheet;
function setupSaveActivity() {
  if (debugEnabled > 0) warn("MidiQOL | SaveActivity | setupSaveActivity | Called");
  MidiSaveSheet = defineMidiSaveSheetClass(game.system.applications.activity.SaveSheet);
  MidiSaveActivity = defineMidiSaveActivityClass(GameSystemConfig.activityTypes.save.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.save = { documentClass: MidiSaveActivity };
  } else {
    GameSystemConfig.activityTypes["midiSave"] = { documentClass: MidiSaveActivity };
  }
}
__name(setupSaveActivity, "setupSaveActivity");
var defineMidiSaveActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiSaveActivity2 extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiSaveActivity");
    }
    static LOCALIZATION_PREFIXES = [...super.LOCALIZATION_PREFIXES, "DND5E.DAMAGE", "midi-qol.SAVE"];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.SAVE.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiSaveSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          actions: {
            rollDamage: MidiSaveActivity2.#rollDamage,
            rollSave: MidiSaveActivity2.#rollSave
            // Having this means needing to track the dnd5e SaveActivity.#rollSave changes
            // Currently needed to tag the request id for midi-qol to pick up chat card saves as being for the current activity
          }
        }
      },
      { inplace: false, overwrite: true, insertKeys: true, insertValues: true }
    );
    static defineSchema() {
      const { StringField: StringField2, ArrayField, BooleanField, SchemaField, ObjectField } = foundry.data.fields;
      const dataModels = game.system.dataModels;
      const {
        ActivationField,
        CreatureTypeField,
        CurrencyTemplate,
        DamageData,
        DamageField,
        DurationField,
        MovementField,
        RangeField,
        RollConfigField,
        SensesField,
        SourceField,
        TargetField,
        UsesField
      } = dataModels.shared;
      const FormulaField = dataModels.fields.FormulaField;
      return {
        ...super.defineSchema(),
        otherActivityId: new StringField2({ name: "otherActivity", initial: "none" }),
        otherActivityAsParentType: new BooleanField({
          name: "otherActivityAsParentType",
          initial: true,
          required: false,
          label: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.label"),
          hint: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.hint")
        }),
        damage: new SchemaField({
          onSave: new StringField2({
            initial: "half",
            default: "half",
            name: "onSave",
            hint: i18n("DND5E.SAVE.FIELDS.damage.onSave.hint"),
            label: i18n("DND5E.SAVE.FIELDS.damage.onSave.label")
          }),
          critical: new SchemaField({
            allow: new BooleanField(),
            bonus: new FormulaField()
          }),
          parts: new ArrayField(new DamageField())
        }),
        friendlySave: new StringField2({ initial: "default", name: "friendlySave" })
        // WIP
        // saveDisplay: new StringField({initial: "default"})
      };
    }
    static #rollDamage(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow }, {}, {});
    }
    static async #rollSave(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      const targets = getSceneTargets();
      if (!targets.length && game.user?.character) targets.push(game.user?.character);
      if (!targets.length) ui.notifications?.warn("DND5E.ActionWarningNoToken", { localize: true });
      const dc = parseInt(target.dataset.dc ?? "0");
      for (const token of targets) {
        const actor = token instanceof Actor ? token : token.actor;
        const speaker = ChatMessage.getSpeaker({ actor, scene: canvas.scene, token: token instanceof Token ? token.document : null });
        const message2 = { data: { speaker } };
        if (token instanceof foundry.canvas.placeables.Token) foundry.utils.setProperty(message2, "flags.midi-qol.requestId", token.document.uuid);
        else foundry.utils.setProperty(message2, "flags.midi-qol.requestId", token?.uuid);
        if (["none", "allShow"].includes(configSettings.autoCheckSaves) || actor?.hasPlayerOwner) message2.rollMode = game.settings.get("core", "rollMode");
        else message2.rollMode = CONST.DICE_ROLL_MODES.PRIVATE;
        if (!game.user?.isGM) {
          if (configSettings.playerRollSaves === "noneDialogPublic") message2.rollMode = CONST.DICE_ROLL_MODES.PUBLIC;
          else if (configSettings.playerRollSaves === "noneDialogPrivate") message2.rollMode = CONST.DICE_ROLL_MODES.PRIVATE;
          else if (configSettings.playerRollSaves === "noneDialogSelf") message2.rollMode = CONST.DICE_ROLL_MODES.SELF;
          else if (configSettings.playerRollSaves === "noneDialogBlind") message2.rollMode = CONST.DICE_ROLL_MODES.BLIND;
          else message2.rollMode = game.settings.get("core", "rollMode");
        }
        await actor?.rollSavingThrow({
          event,
          workflow,
          //@ts-expect-error this has been bound to an activity
          ability: target.dataset.ability ?? this.save.ability.first(),
          //@ts-expect-error this has been bound to an activity
          target: configSettings.autoCheckSaves === "whisper" ? void 0 : Number.isFinite(dc) ? dc : this.save.dc.value
        }, {}, message2);
      }
    }
    use(config = {}, dialog = {}, message = {}) {
      return super.use(config, dialog, message);
    }
    get possibleOtherActivity() {
      return true;
    }
    get canUseOtherActivity() {
      return true;
    }
    async _triggerSubsequentActions(config, results) {
    }
    getDamageConfig(config = {}) {
      const rollConfig = super.getDamageConfig(config);
      rollConfig.critical ??= {};
      rollConfig.critical.allow = this.damage?.critical.allow;
      rollConfig.critical.bonusDamage = this.damage?.critical.bonus;
      return rollConfig;
    }
    async rollDamage(config = {}, dialog = {}, message = {}) {
      message = foundry.utils.mergeObject({
        "data.flags.dnd5e.roll": {
          damageOnSave: this.damage?.onSave
        }
      }, message);
      config.midiOptions ??= {};
      config.midiOptions.fastForwardDamage ??= isAutoFastForward2("damage");
      return super.rollDamage(config, dialog, message);
    }
  };
}, "defineMidiSaveActivityClass");
var defineMidiSaveSheetClass = /* @__PURE__ */ __name((baseClass) => {
  return class MidiSaveSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiSaveSheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/save-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates,
          "modules/midi-qol/templates/activity/parts/save-extras.hbs",
          "modules/midi-qol/templates/activity/parts/save-damage.hbs"
        ]
      }
    };
    static DEFAULT_OPTIONS = {
      ...super.DEFAULT_OPTIONS,
      classes: ["save-activity", "damage-activity"]
    };
    async _prepareContext(options) {
      await this.activity.prepareData({});
      const context = await super._prepareContext(options);
      context.FriendlySaveOptions = [
        { value: "default", label: i18n("midi-qol.SAVE.friendlySave.default") },
        { value: "friendlySuccess", label: i18n("midi-qol.SAVE.friendlySave.friendlySuccess") },
        { value: "friendlyFail", label: i18n("midi-qol.SAVE.friendlySave.friendlyFail") }
      ];
      return context;
    }
  };
}, "defineMidiSaveSheetClass");

// src/module/activities/UtilityActivity.ts
var MidiUtilityActivity;
var MidiUtilitySheet;
function setupUtilityActivity() {
  if (debugEnabled > 0) warn("MidiQOL | UtilityActivity | setupUtilityActivity | Called");
  MidiUtilitySheet = defineMidiUtilitySheetClass(game.system.applications.activity.UtilitySheet);
  MidiUtilityActivity = defineMidiUtilityActivityClass(GameSystemConfig.activityTypes.utility.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.utility = { documentClass: MidiUtilityActivity };
  } else {
    GameSystemConfig.activityTypes["midiUtility"] = { documentClass: MidiUtilityActivity };
  }
}
__name(setupUtilityActivity, "setupUtilityActivity");
var defineMidiUtilityActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiUtilityActivity2 extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiUtilityActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.UTILITY", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.UTILITY.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiUtilitySheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          actions: {
            rollFormula: MidiUtilityActivity2.#rollFormula
          }
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    static defineSchema() {
      const { StringField: StringField2, ArrayField, BooleanField, SchemaField, ObjectField } = foundry.data.fields;
      const dataModels = game.system.dataModels;
      const {
        ActivationField,
        CreatureTypeField,
        CurrencyTemplate,
        DamageData,
        DamageField,
        DurationField,
        MovementField,
        RangeField,
        RollConfigField,
        SensesField,
        SourceField,
        TargetField,
        UsesField
      } = dataModels.shared;
      const schema = {
        ...super.defineSchema(),
        otherActivityId: new StringField2({ name: "otherActivity", initial: "none" }),
        otherActivityAsParentType: new BooleanField({
          name: "otherActivityAsParentType",
          initial: true,
          required: false,
          label: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.label"),
          hint: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.hint")
        })
      };
      return schema;
    }
    static #rollFormula(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollFormula?.({ event, workflow }, {}, {});
    }
    get possibleOtherActivity() {
      return true;
    }
    async _triggerSubsequentActions(config, results) {
    }
    async rollFormula(config, dialog, message) {
      if (debugEnabled > 0)
        warn("UtilityActivity | rollFormula | Called", config, dialog, message);
      config ??= {};
      dialog ??= {};
      message ??= {};
      const workflow = config.workflow;
      if (!workflow) {
        const errorMessage = "MidiUtilityActivity | rollFormula | No workflow found";
        error2(errorMessage);
        TroubleShooter.recordError(new Error("No Workflow Found"), errorMessage);
        return;
      }
      config.midiOptions ??= {};
      config.midiOptions.fastForwardDamage ??= isAutoFastForward2("damage", { workflow });
      if (debugEnabled > 0) {
        warn("MidiUtilityActivity | rollFormula | Called", config, dialog, message);
      }
      if (await callActivityHooks(this, "midi-qol.preFormulaRoll", workflow) === false) {
        console.warn("midi-qol | UtilityActivity | Formula roll blocked via pre-hook");
        return;
      }
      const areKeysPressed = game.system.utils.areKeysPressed;
      const keys = {
        normal: areKeysPressed(config.event, "skipDialogNormal") || areKeysPressed(config.event, "skipDialogAdvantage") || areKeysPressed(config.event, "skipDialogDisadvantage")
      };
      if (Object.values(keys).some((k) => k)) dialog.configure = this.midiProperties?.forceDamageDialog !== "always";
      else if (dialog.configure === void 0) {
        switch (this.midiProperties?.forceDamageDialog) {
          case "always":
            dialog.configure = true;
            break;
          case "never":
            dialog.configure = false;
            break;
          default:
            dialog.configure = !(config.midiOptions?.fastForwardDamage ?? config.workflow?.workflowOptions.fastForwardDamage ?? false);
        }
      }
      if (workflow.rollOptions?.rollToggle) dialog.configure = !!!dialog.configure;
      const rollToggle = areMidiKeysPressed(config.event, "RollToggle");
      if (workflow) workflow.rollOptions.rollToggle = rollToggle;
      if (rollToggle) dialog.configure = !!!dialog.configure;
      message.create ??= false;
      let result = await super.rollFormula(config, dialog, message);
      if (result && workflow.workflowOptions?.formulaDSN !== false) await displayDSNForRoll(result[0], "formulaD20");
      if (config.midiOptions.updateWorkflow !== false && workflow) {
        if (result) await workflow.setUtilityRolls(result);
        if (workflow.suspended)
          workflow.unSuspend.bind(workflow)({ utilityRolls: result });
      }
      return result;
    }
    async _usageChatContext(message) {
      const context = await super._usageChatContext(message);
      context.hasRollFormula = true;
      return context;
    }
  };
}, "defineMidiUtilityActivityClass");
function defineMidiUtilitySheetClass(baseClass) {
  return class MidiUtilitySheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiUtilitySheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/utility-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates
        ]
      }
    };
  };
}
__name(defineMidiUtilitySheetClass, "defineMidiUtilitySheetClass");

// src/module/activities/DamageActivity.ts
var MidiDamageActivity;
var MidiDamageSheet;
function setupDamageActivity() {
  if (debugEnabled > 0) warn("MidiQOL | DamageActivity | setupDamageActivity | Called");
  MidiDamageSheet = defineMidiDamageSheetClass(game.system.applications.activity.DamageSheet);
  MidiDamageActivity = defineMidiDamageActivityClass(GameSystemConfig.activityTypes.damage.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.damage = { documentClass: MidiDamageActivity };
  } else {
    GameSystemConfig.activityTypes["midiDamage"] = { documentClass: MidiDamageActivity };
  }
}
__name(setupDamageActivity, "setupDamageActivity");
var defineMidiDamageActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiDamageActivity2 extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiDamageActivity");
    }
    static LOCALIZATION_PREFIXES = [...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.DAMAGE.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiDamageSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          actions: {
            rollDamage: MidiDamageActivity2.#rollDamage
          }
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    static #rollDamage(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      this.rollDamage({ event, workflow }, {}, {});
    }
    get possibleOtherActivity() {
      return true;
    }
    get canUseOtherActivity() {
      return true;
    }
    async rollDamage(config, dialog, message) {
      config.midiOptions ??= {};
      config.midiOptions.fastForwardDamage ??= isAutoFastForward2("damage");
      return super.rollDamage(config, dialog, message);
    }
    async _triggerSubsequentActions(config, results) {
    }
  };
}, "defineMidiDamageActivityClass");
function defineMidiDamageSheetClass(baseClass) {
  return class MidiDamageSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiDamageSheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/damage-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates
        ]
      }
    };
  };
}
__name(defineMidiDamageSheetClass, "defineMidiDamageSheetClass");

// src/module/activities/CastActivity.ts
var MidiCastActivity;
var MidiCastSheet;
function setupCastActivity() {
  if (debugEnabled > 0) warn("MidiQOL | CastActivity | setupCastActivity | Called");
  MidiCastSheet = defineMidiCastSheetClass(game.system.applications.activity.CastSheet);
  MidiCastActivity = defineMidiCastActivityClass(GameSystemConfig.activityTypes.cast.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.cast = { documentClass: MidiCastActivity };
  } else {
    GameSystemConfig.activityTypes["midiCast"] = { documentClass: MidiCastActivity };
  }
}
__name(setupCastActivity, "setupCastActivity");
var defineMidiCastSheetClass = /* @__PURE__ */ __name((baseClass) => {
  return class MidiCastSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiCastSheet");
    }
  };
}, "defineMidiCastSheetClass");
var defineMidiCastActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiCastActivity extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiCastActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.CAST", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.CAST.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiCastSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          dialog: ActivityClass.metadata.usage.dialog
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    /** Resolve the effective activation type for Cast activities.
     *  1. If activation.override is set, use the Cast activity's own activation type.
     *  2. Otherwise, use the linked spell's first usable activity's activation type.
     *  3. Fall back to the parent item's activation type.
     */
    get effectiveActivationType() {
      if (this.activation?.override) return this.activation.type;
      const spell = this.cachedSpell;
      if (spell) {
        const activities = spell.system?.activities;
        const activityList = activities instanceof Collection ? activities.contents : Object.values(activities ?? {});
        const spellActivity = activityList.find((a) => a.canUse) ?? activityList[0];
        if (spellActivity?.activation?.type) return spellActivity.activation.type;
      }
      return this.item?.system?.activation?.type ?? this.activation?.type;
    }
    async use(usage = {}, dialog = {}, message = {}) {
      if (!this.item.isEmbedded || !this.item.isOwner || !this.canUse) {
        return ActivityClass.prototype.use.bind(this)(usage, dialog, message);
      }
      usage.midiOptions ??= {};
      usage.midiOptions.workflowOptions ??= {};
      usage.midiOptions.proceedChecks ??= {};
      const speaker = ChatMessage.getSpeaker({ actor: this.item.actor });
      const tempWorkflow = new Workflow(this.actor ?? null, this, speaker, null, { storeWorkflow: false });
      usage.workflow = tempWorkflow;
      try {
        if (!await this.preTargetingChecks(usage, dialog, message)) return false;
        if (!await this.preChatChecks(usage, dialog, message, { checkComponents: false })) return false;
        const castUsesReaction = tempWorkflow.itemUsesReaction;
        const castUsesBonusAction = tempWorkflow.itemUsesBonusAction;
        Workflow.removeWorkflow(tempWorkflow.id);
        delete usage.workflow;
        usage.midiOptions.proceedChecks.checkReaction = false;
        usage.midiOptions.proceedChecks.checkBonusAction = false;
        usage.midiOptions.workflowOptions.castUsesReaction = castUsesReaction;
        usage.midiOptions.workflowOptions.castUsesBonusAction = castUsesBonusAction;
      } catch (err) {
        Workflow.removeWorkflow(tempWorkflow.id);
        delete usage.workflow;
        throw err;
      }
      return await ActivityClass.prototype.use.bind(this)(usage, dialog, message);
    }
    get possibleOtherActivity() {
      return false;
    }
    get isSelfTriggerableOnly() {
      return false;
    }
    get isTriggerableActivity() {
      return true;
    }
    get forcedTargetConfirmation() {
      return "never";
    }
    async _triggerSubsequentActions(config, results) {
    }
  };
}, "defineMidiCastActivityClass");

// src/module/activities/CheckActivity.ts
var MidiCheckActivity;
var MidiCheckSheet;
function setupCheckActivity() {
  if (debugEnabled > 0) warn("MidiQOL | CheckActivity | setupCheckActivity | Called");
  MidiCheckSheet = defineMidiCheckSheetClass(game.system.applications.activity.CheckSheet);
  MidiCheckActivity = defineMidiCheckActivityClass(GameSystemConfig.activityTypes.check.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.check = { documentClass: MidiCheckActivity };
  } else {
    GameSystemConfig.activityTypes["midiCheck"] = { documentClass: MidiCheckActivity };
  }
}
__name(setupCheckActivity, "setupCheckActivity");
var defineMidiCheckActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiCheckActivity2 extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiCheckActivity");
    }
    static LOCALIZATION_PREFIXES = [...super.LOCALIZATION_PREFIXES, "midi-qol.SAVE", "midi-qol.CHECK", "DND5E.DAMAGE"];
    static superMetadata = super.metadata;
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.CHECK.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiCheckSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          actions: {
            rollCheck: MidiCheckActivity2.#rollCheck,
            // Having this means having to track CheckActivity.#rollCheck code changes
            // Needed so the workflow can be inserted
            rollDamage: MidiSaveActivity.metadata.usage.actions.rollDamage
          }
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    static defineSchema() {
      const { StringField: StringField2, ArrayField, BooleanField, SchemaField, ObjectField } = foundry.data.fields;
      const dataModels = game.system.dataModels;
      const {
        ActivationField,
        CreatureTypeField,
        CurrencyTemplate,
        DamageData,
        DamageField,
        DurationField,
        MovementField,
        RangeField,
        RollConfigField,
        SensesField,
        SourceField,
        TargetField,
        UsesField
      } = dataModels.shared;
      const FormulaField = dataModels.fields.FormulaField;
      const schema = {
        ...super.defineSchema(),
        otherActivityId: new StringField2({ name: "otherActivity", initial: "none" }),
        otherActivityAsParentType: new BooleanField({
          name: "otherActivityAsParentType",
          initial: true,
          required: false,
          label: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.label"),
          hint: i18n("midi-qol.SHARED.FIELDS.otherActivityAsParentType.hint")
        }),
        damage: new SchemaField({
          onSave: new StringField2({
            initial: "half",
            default: "half",
            name: "onSave",
            hint: i18n("DND5E.SAVE.FIELDS.damage.onSave.hint"),
            label: i18n("DND5E.SAVE.FIELDS.damage.onSave.label")
          }),
          parts: new ArrayField(new DamageField()),
          critical: new SchemaField({
            allow: new BooleanField(),
            bonus: new FormulaField()
          })
        }),
        friendlySave: new StringField2({ initial: "default", name: "friendlySave" })
        // WIP
        // saveDisplay: new StringField({initial: "default"}),
      };
      return schema;
    }
    static async #rollCheck(event, target, message) {
      let workflow = message ? Workflow.getWorkflow(message.uuid) : null;
      if (workflow) workflow.activity = this;
      const targets = getSceneTargets();
      if (!targets.length && game.user?.character) targets.push(game.user?.character);
      if (!targets.length) ui.notifications?.warn("DND5E.ActionWarningNoToken", { localize: true });
      let { ability, dc: dcStr, skill, tool } = target.dataset;
      let dc = parseInt(dcStr ?? "0");
      const check = this.check;
      const item = this.item;
      const rollData = { event, target: Number.isFinite(dc) ? dc : check.dc.value };
      if (ability && ability in globalThis.dnd5e.config.abilities) rollData.ability = ability;
      for (const token of targets) {
        const actor = token instanceof Actor ? token : token.actor;
        const speaker = ChatMessage.getSpeaker({ actor, scene: canvas.scene, token: token instanceof Token ? token.document : null });
        const messageData = { data: { speaker } };
        if (!actor) continue;
        if (["none", "allShow"].includes(configSettings.autoCheckSaves) || actor?.hasPlayerOwner) messageData.rollMode = game.settings.get("core", "rollMode");
        else messageData.rollMode = CONST.DICE_ROLL_MODES.PRIVATE;
        if (!game.user?.isGM) {
          if (configSettings.playerRollSaves === "noneDialogPublic") messageData.rollMode = CONST.DICE_ROLL_MODES.PUBLIC;
          else if (configSettings.playerRollSaves === "noneDialogPrivate") messageData.rollMode = CONST.DICE_ROLL_MODES.PRIVATE;
          else if (configSettings.playerRollSaves === "noneDialogSelf") messageData.rollMode = CONST.DICE_ROLL_MODES.SELF;
          else if (configSettings.playerRollSaves === "noneDialogBlind") messageData.rollMode = CONST.DICE_ROLL_MODES.BLIND;
          else messageData.rollMode = game.settings.get("core", "rollMode");
        }
        if (skill) await actor.rollSkill({ ...rollData, skill }, {}, messageData);
        else if (tool) {
          rollData.tool = tool;
          if (item.type === "tool" && !check.associated.size) {
            rollData.bonus = item.system.bonus;
            rollData.prof = item.system.prof;
            rollData.item = item;
          }
          await actor.rollToolCheck(rollData, {}, messageData);
        } else await actor.rollAbilityCheck(rollData, {}, messageData);
      }
    }
    get possibleOtherActivity() {
      return true;
    }
    get canUseOtherActivity() {
      return true;
    }
    get isSelfTriggerableOnly() {
      return false;
    }
    async _triggerSubsequentActions(config, results) {
    }
    async rollDamage(config, dialog = {}, message = {}) {
      message = foundry.utils.mergeObject({
        "data.flags.dnd5e.roll": {
          damageOnSave: this.damage?.onSave
        }
      }, message);
      return super.rollDamage(config, dialog, message);
    }
    prepareFinalData(rollData) {
      super.prepareFinalData(rollData);
    }
    _usageChatButtons(message) {
      const buttons = [];
      if (this.damage.parts.length) buttons.push({
        label: i18n("DND5E.Damage"),
        icon: '<i class="fas fa-burst" inert></i>',
        dataset: {
          action: "rollDamage"
        }
      });
      const superButtons = super._usageChatButtons(message);
      const hasCheckButton = superButtons.some((b) => b.dataset?.action === "rollCheck");
      if (!hasCheckButton) {
        const dc = this.check?.dc.value;
        const label = i18n("midi-qol.plain-d20-roll");
        const dataset = { action: "rollCheck", visibility: "all" };
        if (dc) dataset.dc = dc;
        buttons.push({
          label: dc ? `<span class="visible-dc">${game.i18n.format("EDITOR.DND5E.Inline.DC", { dc: String(dc), check: label })}</span><span class="hidden-dc">${label}</span>` : label,
          icon: '<i class="dnd5e-icon" data-src="systems/dnd5e/icons/svg/ability-score-improvement.svg" inert></i>',
          dataset
        });
      }
      return buttons.concat(superButtons);
    }
  };
}, "defineMidiCheckActivityClass");
function defineMidiCheckSheetClass(baseClass) {
  return class MidiCheckSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiCheckSheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/check-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates,
          "modules/midi-qol/templates/activity/parts/save-extras.hbs",
          "modules/midi-qol/templates/activity/parts/save-damage.hbs",
          "systems/dnd5e/templates/activity/parts/damage-part.hbs",
          "systems/dnd5e/templates/activity/parts/damage-parts.hbs"
        ]
      }
    };
    async _prepareEffectContext(context) {
      context = await super._prepareEffectContext(context);
      context.FriendlySaveOptions = [
        { value: "default", label: i18n("midi-qol.SAVE.friendlySave.default") },
        { value: "friendlySuccess", label: i18n("midi-qol.SAVE.friendlySave.friendlySuccess") },
        { value: "friendlyFail", label: i18n("midi-qol.SAVE.friendlySave.friendlyFail") }
      ];
      context.onSaveOptions = [
        { value: "none", label: i18n("DND5E.SAVE.FIELDS.damage.onSave.None") },
        { value: "half", label: i18n("DND5E.SAVE.FIELDS.damage.onSave.Half") },
        { value: "full", label: i18n("DND5E.SAVE.FIELDS.damage.onSave.Full") }
      ];
      let autoCheckOptions = foundry.utils.duplicate(autoCheckSavesOptions);
      delete autoCheckOptions["none"];
      context.SaveDisplayOptions = Object.keys(autoCheckOptions).reduce((acc, key) => {
        acc.push({ value: key, label: autoCheckOptions[key] });
        return acc;
      }, [{ value: "default", label: i18n("Default") }]);
      return context;
    }
  };
}
__name(defineMidiCheckSheetClass, "defineMidiCheckSheetClass");

// src/module/activities/HealActivity.ts
var MidiHealActivity;
var MidiHealSheet;
function setupHealActivity() {
  if (debugEnabled > 0) warn("MidiQOL | HealActivity | setupHealActivity | Called");
  MidiHealSheet = defineMidiHealSheetClass(game.system.applications.activity.HealSheet);
  MidiHealActivity = defineMidiHealActivityClass(GameSystemConfig.activityTypes.heal.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.heal = { documentClass: MidiHealActivity };
  } else {
    GameSystemConfig.activityTypes["midiHeal"] = { documentClass: MidiHealActivity };
  }
}
__name(setupHealActivity, "setupHealActivity");
var defineMidiHealActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiHealActivity2 extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiHealActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.HEAL", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.HEAL.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiHealSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs",
          actions: {
            rollHealing: MidiHealActivity2.#rollHealing
          }
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    static #rollHealing(event, target, message) {
      const workflow = Workflow.getWorkflow(message?.uuid);
      if (workflow) workflow.activity = this;
      return this.rollDamage({ event, workflow }, {}, {});
    }
    get possibleOtherActivity() {
      return true;
    }
    get canUseOtherActivity() {
      return true;
    }
    async rollDamage(config = {}, dialog = {}, message = {}) {
      config.midiOptions ??= {};
      config.midiOptions.fastForwardHeal ??= isAutoFastForward2("damage");
      config.midiOptions.fastForwardDamage ??= isAutoFastForward2("damage");
      return super.rollDamage(config, dialog, message);
    }
    /*
      getDamageConfig(config: any ={}) {
        if ( !this.healing.formula ) return foundry.utils.mergeObject({ rolls: [] }, config);
    
        const rollConfig:any = foundry.utils.mergeObject({ critical: { allow: false }, scaling: 0 }, config);
        const rollData = this.getRollData();
        rollConfig.rolls = [this._processDamagePart(this.healing, rollConfig, rollData)].concat(config.rolls ?? []);
    
        return rollConfig;
      }
      */
    async _triggerSubsequentActions(config, results) {
    }
  };
}, "defineMidiHealActivityClass");
function defineMidiHealSheetClass(baseClass) {
  return class MidiHealSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiHealSheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/heal-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates
        ]
      }
    };
  };
}
__name(defineMidiHealSheetClass, "defineMidiHealSheetClass");

// src/module/activities/ForwardActivity.ts
var MidiForwardActivity;
var MidiForwardSheet;
function setupForwardActivity() {
  if (debugEnabled > 0) warn("MidiQOL | ForwardActivity | setupForwardActivity | Called");
  MidiForwardSheet = defineMidiForwardSheetClass(game.system.applications.activity.ForwardSheet);
  MidiForwardActivity = defineMidiForwardActivityClass(GameSystemConfig.activityTypes.forward.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.forward = { documentClass: MidiForwardActivity };
  } else {
    GameSystemConfig.activityTypes["midiForward"] = { documentClass: MidiForwardActivity };
  }
}
__name(setupForwardActivity, "setupForwardActivity");
var defineMidiForwardSheetClass = /* @__PURE__ */ __name((baseClass) => {
  return class MidiForwardSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiForwardSheet");
    }
  };
}, "defineMidiForwardSheetClass");
var defineMidiForwardActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiForwardActivity extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiForwardActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.FORWARD", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.FORWARD.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiForwardSheet,
        usage: {
          chatCard: "modules/midi-qol/templates/activity-card.hbs"
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    get possibleOtherActivity() {
      return false;
    }
    get effects() {
      return [];
    }
    /** @override */
    async use(usage = {}, dialog = {}, message = {}) {
      const usageConfig = foundry.utils.mergeObject({
        cause: {
          activity: this.relativeUUID
        },
        consume: {
          resources: false,
          spellSlot: false
        }
      }, usage);
      const activity = this.item.system.activities.get(this.activity.id);
      if (!activity) ui.notifications?.error("DND5E.FORWARD.Warning.NoActivity", { localize: true });
      return activity?.use(usageConfig, dialog, message);
    }
    get isSelfTriggerableOnly() {
      return false;
    }
    get isTriggerableActivity() {
      return false;
    }
    get forcedTargetConfirmation() {
      return "never";
    }
    async _triggerSubsequentActions(config, results) {
    }
  };
}, "defineMidiForwardActivityClass");

// src/module/activities/EnchantActivity.ts
var MidiEnchantActivity;
var MidiEnchantSheet;
function setupEnchantActivity() {
  if (debugEnabled > 0) warn("MidiQOL | EnchantActivity | setupEnchantActivity | Called");
  MidiEnchantSheet = defineMidiEnchantSheetClass(game.system.applications.activity.EnchantSheet);
  MidiEnchantActivity = defineMidiEnchantActivityClass(GameSystemConfig.activityTypes.enchant.documentClass);
  if (replaceDefaultActivities) {
    GameSystemConfig.activityTypes.enchant = { documentClass: MidiEnchantActivity };
  } else {
    GameSystemConfig.activityTypes["midiEnchant"] = { documentClass: MidiEnchantActivity };
  }
}
__name(setupEnchantActivity, "setupEnchantActivity");
var defineMidiEnchantActivityClass = /* @__PURE__ */ __name((ActivityClass) => {
  return class MidiEnchantActivity extends MidiActivityMixin(ActivityClass) {
    static {
      __name(this, "MidiEnchantActivity");
    }
    static LOCALIZATION_PREFIXES = ["midi-qol.ENCHANT", ...super.LOCALIZATION_PREFIXES];
    static metadata = foundry.utils.mergeObject(
      super.metadata,
      {
        title: configSettings.activityNamePrefix ? "midi-qol.ENCHANT.Title.one" : ActivityClass.metadata.title,
        dnd5eTitle: ActivityClass.metadata.title,
        sheetClass: MidiEnchantSheet,
        usage: {
          dialog: ActivityClass.metadata.usage.dialog
        }
      },
      { inplace: false, insertKeys: true, insertValues: true }
    );
    get possibleOtherActivity() {
      return false;
    }
    get isTriggerableActivity() {
      return true;
    }
    async _triggerSubsequentActions(config, results) {
      await super._triggerSubsequentActions(config, results);
    }
  };
}, "defineMidiEnchantActivityClass");
function defineMidiEnchantSheetClass(baseClass) {
  return class MidiEnchantSheet extends MidiActivityMixinSheet(baseClass) {
    static {
      __name(this, "MidiEnchantSheet");
    }
    static PARTS = {
      ...super.PARTS,
      effect: {
        template: "modules/midi-qol/templates/activity/enchant-effect.hbs",
        templates: [
          ...super.PARTS.effect.templates
        ]
      }
    };
  };
}
__name(defineMidiEnchantSheetClass, "defineMidiEnchantSheetClass");

// src/module/MidiCombat5e.ts
var MidiCombat5e = class extends globalThis.dnd5e.documents.Combat5e {
  static {
    __name(this, "MidiCombat5e");
  }
  async endCombat() {
    if (configSettings.activationAutomation !== "auto") return super.endCombat();
    await Reflect.apply(Combat.prototype.endCombat, this, []);
    super._recoverUses({ turn: true, turnEnd: true, turnStart: false });
    return this;
  }
};

// src/module/ChatClasses/DamageApplicationElementMidi.ts
var MIDI_CHANGE_ICONS = {
  saved: "systems/dnd5e/icons/svg/activity/save.svg",
  superSaver: "systems/dnd5e/icons/svg/checked-shield.svg",
  semiSuperSaver: "systems/dnd5e/icons/svg/checked-shield.svg",
  uncannyDodge: "systems/dnd5e/icons/svg/activity/utility.svg",
  absorption: "systems/dnd5e/icons/svg/damage/healing.svg",
  DR: "systems/dnd5e/icons/svg/damage/threshold.svg",
  spell: "systems/dnd5e/icons/svg/activity/cast.svg",
  magic: "systems/dnd5e/icons/svg/activity/enchant.svg",
  nonmagic: "systems/dnd5e/icons/svg/activity/attack.svg"
};
var DamageApplicationElementMidi = class _DamageApplicationElementMidi extends globalThis.dnd5e.applications.components.DamageApplicationElement {
  static {
    __name(this, "DamageApplicationElementMidi");
  }
  #targetOptions = /* @__PURE__ */ new Map();
  chatMessage;
  damages = [];
  damageType = "defaultDamage";
  /**
   * Override to handle "all" type for consolidated save icons and other midi custom types
   */
  getChangeSourceButton({ change, icon, type }, targetOptions) {
    if (type === "all" || !GameSystemConfig.damageTypes[type]) {
      const customIcon = icon || MIDI_CHANGE_ICONS[change];
      if (customIcon) {
        const { label, pressed } = this.getChangeSourceOptions(type, change, targetOptions);
        return `
          <button class="change-source unbutton" type="button" data-type="${type}" data-change="${change}"
                  data-tooltip aria-label="${label}" aria-pressed="${pressed}">
            <dnd5e-icon src="${customIcon}" inert></dnd5e-icon>
            <i class="fa-solid fa-slash" inert></i>
            <i class="fa-solid fa-arrow-turn-down" inert></i>
          </button>
        `;
      }
    }
    return super.getChangeSourceButton({ change, icon, type }, targetOptions);
  }
  /**
   * Override to handle "all" type and midi custom change types for labels
   */
  getChangeSourceOptions(type, change, options) {
    let mode = "active";
    if (options.ignore?.[change] === true || options.ignore?.[change]?.has?.(type)) mode = "ignore";
    else if (change === "immunity" && options.downgrade?.has(type)) mode = "downgrade";
    const changeKey = change.charAt(0).toUpperCase() + change.slice(1);
    const typeLabel = GameSystemConfig.damageTypes[type]?.label ?? GameSystemConfig.healingTypes?.[type]?.label ?? "";
    let label = i18nFormat2(`DND5E.DamageApplication.Change.${changeKey}`, { type: typeLabel });
    if (mode === "ignore") label = i18nFormat2("DND5E.DamageApplication.Ignoring", { source: label });
    if (mode === "downgrade") label = i18nFormat2("DND5E.DamageApplication.Downgrading", { source: label });
    return { label, pressed: mode === "active" ? "false" : mode === "ignore" ? "true" : "mixed" };
  }
  /**
   * Override to append midi custom change source buttons after parent builds the entry
   */
  buildTargetListEntry({ uuid, name }) {
    const li = super.buildTargetListEntry({ uuid, name });
    if (!li) return;
    const targetOptions = this.getTargetOptions(uuid);
    const actor = fromUuidSync(uuid);
    if (!actor) return li;
    const { active } = this.calculateDamage(actor, targetOptions);
    const subtitle = li.querySelector(".subtitle");
    if (!subtitle) return li;
    for (const [change, values] of Object.entries(active)) {
      if (foundry.utils.getType(values) !== "Set") continue;
      for (const type of values) {
        if (GameSystemConfig.damageTypes[type] || GameSystemConfig.healingTypes?.[type]) continue;
        if (MIDI_CHANGE_ICONS[change]) {
          const button = this.getChangeSourceButton({ type, change, icon: MIDI_CHANGE_ICONS[change] }, targetOptions);
          subtitle.insertAdjacentHTML("beforeend", button);
        }
      }
    }
    return li;
  }
  getTargetOptions(uuid) {
    if (!this.#targetOptions.has(uuid)) this.#targetOptions.set(uuid, { multiplier: 1 });
    const options = this.#targetOptions.get(uuid);
    if (!options) return { multiplier: 1 };
    let sourceActorUuid = foundry.utils.getProperty(this.chatMessage, "flags.midi-qol.sourceActorUuid");
    try {
      let targetDetails;
      const targets = foundry.utils.getProperty(this.chatMessage, "flags.dnd5e.targets" /* targetDescriptors */) ?? [];
      targetDetails = targets.find((target) => target.uuid === uuid);
      if (!targetDetails) return options;
      options.midi = foundry.utils.duplicate(targetDetails);
      const saveMultiplier = targetDetails.saveMults?.[this.damageType];
      if (targetDetails.saved) {
        foundry.utils.setProperty(options, "midi.saveMultiplier", saveMultiplier ?? configSettings.defaultSaveMultiplier);
      }
      if (saveMultiplier !== void 0) {
        if (targetDetails.superSaver && saveMultiplier === configSettings.defaultSaveMultiplier) {
          foundry.utils.setProperty(options, "midi.saveMultiplier", targetDetails.saved ? 0 : configSettings.defaultSaveMultiplier);
        }
        if (targetDetails.semiSuperSaver && saveMultiplier === configSettings.defaultSaveMultiplier) {
          foundry.utils.setProperty(options, "midi.saveMultiplier", targetDetails.saved ? 0 : 1);
        }
      }
      if (targetDetails.uncannyDodge) {
        foundry.utils.setProperty(options, "midi.uncannyDodge", true);
      }
      if (targetDetails.sourceActorUuid) {
        sourceActorUuid = targetDetails.sourceActorUuid;
      }
    } catch (err) {
      const message = `midi-qol | _DAgetTargetOptions failed to get target options`;
      console.warn(message, err);
      TroubleShooter.recordError(err, message);
    }
    foundry.utils.setProperty(options, "midi.sourceActorUuid", sourceActorUuid);
    return options;
  }
  // Flag categories for active damage modifiers
  static SET_FLAGS = [
    "modification",
    "resistance",
    "vulnerability",
    "immunity",
    "absorption",
    "spell",
    "magic",
    "nonmagic",
    "DR",
    "uncannyDodge"
  ];
  static SAVE_FLAGS = ["saved", "superSaver", "semiSuperSaver"];
  // Use "all" type for consolidated icon
  static BOOL_FLAGS = ["threshold"];
  calculateDamage(actor, options) {
    const damages = actor.calculateDamage(this.damages, options);
    const { amount: total, temp: rawTemp, tempMax: rawTempMax } = damages;
    const temp = Math.floor(Math.max(0, rawTemp ?? 0));
    const tempMax = rawTempMax ?? 0;
    const active = {};
    for (const flag of [..._DamageApplicationElementMidi.SET_FLAGS, ..._DamageApplicationElementMidi.SAVE_FLAGS]) {
      active[flag] = /* @__PURE__ */ new Set();
    }
    for (const flag of _DamageApplicationElementMidi.BOOL_FLAGS) {
      active[flag] = false;
    }
    try {
      for (const damage of damages) {
        const isDRType2 = ["none", "midi-none"].includes(damage.type);
        for (const flag of _DamageApplicationElementMidi.SET_FLAGS) {
          if (isDnd5e53) {
            if (damage.active?.type?.[flag]) active[flag].add(damage.type);
            if (damage.active?.all?.[flag]) active[flag].add("ALL");
          } else {
            if (damage.active?.[flag]) active[flag].add(damage.type);
          }
        }
        if (!isDRType2) {
          for (const flag of _DamageApplicationElementMidi.SAVE_FLAGS) {
            if (isDnd5e53) {
              if (damage.active?.type?.[flag] || damage.active?.all?.[flag]) active[flag].add("all");
            } else {
              if (damage.active?.[flag]) active[flag].add("all");
            }
          }
        }
        for (const flag of _DamageApplicationElementMidi.BOOL_FLAGS) {
          if (damage.active?.[flag]) active[flag] = true;
        }
      }
      for (const flag of [..._DamageApplicationElementMidi.SET_FLAGS, ..._DamageApplicationElementMidi.SAVE_FLAGS]) {
        if (foundry.utils.getType(options.ignore?.[flag]) === "Set") {
          active[flag] = active[flag].union(options.ignore?.[flag]);
        }
      }
      if (options.downgrade instanceof Set) {
        active.immunity = active.immunity.union(options.downgrade);
      }
      active.threshold ||= options.ignore?.threshold ?? false;
    } catch (err) {
      const message = `midi-qol | calculateDamage failed to calculate damage`;
      console.warn(message, err);
      TroubleShooter.recordError(err, message);
    }
    return { temp, tempMax, total, active };
  }
};

// src/midi-qol.ts
injectMidiCompletions();
function getCanvas() {
  if (!canvas || !canvas.scene) {
    error2("Canvas/Scene not ready - roll automation will not function");
    return void 0;
  }
  return canvas;
}
__name(getCanvas, "getCanvas");
var ceInterface;
var BaseChatLogClass;
var debugEnabled = 0;
var debug = /* @__PURE__ */ __name((...args) => {
  if (debugEnabled > 1) console.log("DEBUG: midi-qol | ", ...args);
}, "debug");
var log = /* @__PURE__ */ __name((...args) => console.log("midi-qol | ", ...args), "log");
var warn = /* @__PURE__ */ __name((...args) => {
  if (debugEnabled > 0) console.warn("midi-qol | ", ...args);
}, "warn");
var error2 = /* @__PURE__ */ __name((...args) => console.error("midi-qol | ", ...args), "error");
var timelog = /* @__PURE__ */ __name((...args) => warn("midi-qol | ", Date.now(), ...args), "timelog");
var levelsAPI;
var MODULE_ID = "midi-qol";
var { StringField } = foundry.data.fields;
var i18n = /* @__PURE__ */ __name((key) => {
  const k = typeof key === "object" ? key.label : key;
  return game.i18n?.localize(k) ?? k;
}, "i18n");
var i18nFormat2 = /* @__PURE__ */ __name((key, data = {}) => {
  return game.i18n?.format(key, data) ?? key;
}, "i18nFormat");
function getHealingLabel() {
  return game.i18n?.has("DND5E.HEAL.HealingButton") ? i18n("DND5E.HEAL.HealingButton") : i18n("DND5E.Healing");
}
__name(getHealingLabel, "getHealingLabel");
function getStaticID(id) {
  id = `dnd5e${id}`;
  if (id.length >= 16) return id.substring(0, 16);
  return id.padEnd(16, "0");
}
__name(getStaticID, "getStaticID");
var setDebugLevel = /* @__PURE__ */ __name((debugText) => {
  debugEnabled = { "none": 0, "warn": 1, "debug": 2, "all": 3 }[debugText] || 0;
  if (debugEnabled >= 3) CONFIG.debug.hooks = true;
}, "setDebugLevel");
async function busyWait(milliSeconds) {
  return new Promise((resolve) => setTimeout(resolve, milliSeconds));
}
__name(busyWait, "busyWait");
var midiFlags = [];
var daeAddAutoFields = null;
var daeBooleanFormulaField = null;
var midiInitComplete = false;
Hooks.once("dae.addAutoFields", (addAutoFields, fields) => {
  daeAddAutoFields = addAutoFields;
  daeBooleanFormulaField = fields?.BooleanFormulaField;
  if (midiInitComplete) {
    setupMidiFlags();
    daeAddAutoFields(midiFlags.filter((f) => !!f.name));
  }
});
var allAttackTypes = [];
var gameStats;
var overTimeEffectsToDelete = {};
var savedOverTimeEffectsToDelete = {};
var MQItemMacroLabel;
var MQActivityMacroLabel;
var MQOnUseOptions;
var GameSystemConfig;
var systemConcentrationId;
var midiReactionEffect;
var midiBonusActionEffect;
var midiFlankingEffect;
var midiFlankedEffect;
var MESSAGE_TYPES = {
  HITS: 1,
  SAVES: 2,
  ATTACK: 3,
  DAMAGE: 4,
  OTHER: 5,
  ITEM: 0
};
var MQDamageRollTypes = ["defaultDamage", "otherDamage", "bonusDamage"];
Hooks.once("levelsReady", function() {
  levelsAPI = CONFIG.Levels.API;
});
Hooks.once("init", () => {
  registerHandlebarsHelpers();
  setupChatMessageMidi();
  CONFIG.ChatMessage.documentClass = ChatMessageMidi;
  CONFIG.Combat.documentClass = MidiCombat5e;
});
globalThis.MidiQOL ??= {};
Object.assign(globalThis.MidiQOL, {
  activityHasEmanationNoTemplate,
  addDependent,
  addConcentrationDependent,
  addRollTo,
  addUndoChatMessage,
  applyTokenDamage,
  applyTokenDamageMany,
  actorFromUuid,
  canSee,
  canSense,
  canSenseModes,
  checkActivityRange,
  checkDistance,
  checkIncapacitated,
  checkNearby,
  chooseEffect,
  completeItemUse,
  completeActivityUse: completeActivityUse2,
  computeAbilityAdvantage,
  computeCoverBonus,
  computeDistance,
  computeSkillAdvantage,
  computeToolAdvantage,
  contestedRoll,
  createConditionData,
  createDamageDetail,
  createEffects: createEffects2,
  DamageOnlyWorkflow,
  DDBGameLogWorkflow,
  debug,
  displayDSNForRoll,
  doConcentrationCheck,
  doOverTimeEffect,
  DummyWorkflow,
  evalAllConditions,
  evalCondition,
  findNearby,
  findNearbyCount,
  fromUuidSync,
  getCachedChatMessage: getCachedDocument2,
  getChanges: getChanges2,
  getConcentrationEffect,
  getDistance,
  getItemFromEffectOrigin,
  getTokenPlayerName,
  getTokenPlayerNameForUser,
  getTokenForActor: getOrCreateTokenForActor,
  getTokenForActorAsSet: getOrCreateTokenForActorAsSet,
  getTraitMult,
  getUndoQueue,
  getActorWoundedSemaphore,
  getVitalityItem,
  getVitalityMax,
  getVitalityValue,
  hasCondition,
  hasUsedBonusAction,
  hasUsedReaction,
  hasVitalitySystem,
  isTargetable: isValidTarget,
  log,
  MidiDependentsRegistry,
  midiRenderRoll,
  midiRenderAttackRoll,
  midiRenderDamageRoll,
  midiRenderBonusDamageRoll,
  midiRenderOtherDamageRoll,
  MidiSounds,
  modifyDamageBy,
  moveToken,
  moveTokenAwayFromPoint,
  MQfromActorUuid: fromActorUuid,
  MQfromUuid: fromUuidSync,
  playerFor,
  playerForActor,
  processWoundedStatus,
  raceOrType,
  reactionDialog,
  removeActionUsed,
  removeBonusActionUsed,
  removeEffects: removeEffects2,
  removeHiddenCondition,
  removeInvisibleCondition,
  removeMostRecentWorkflow,
  removeReactionUsed,
  resolveItemMacro,
  resolveActivityMacro,
  resolveTargetConfirmation,
  RollModifierTracker,
  setBonusActionUsed,
  setReactionUsed,
  sizeNumeric,
  showUndoQueue,
  showUndoWorkflowApp,
  teleportToken,
  testfunc,
  tokenForActor,
  TrapWorkflow,
  typeOrRace,
  undoMostRecentWorkflow,
  updateEffects: updateEffects2,
  UserWorkflow,
  validRollAbility,
  warn,
  Workflow,
  workflowClass: Workflow,
  Workflows: { "Workflow": Workflow, "DamageOnlyWorkflow": DamageOnlyWorkflow, "TrapWorkflow": TrapWorkflow, "DummyWorkflow": DummyWorkflow, "DDBGameLogWorkflow": DDBGameLogWorkflow },
  ChatClasses: {
    ChatLogMidi,
    ChatMessageMidi,
    DamageApplicationElementMidi
  },
  apps: {
    ActorOnUseMacrosConfig,
    ConditionEditor,
    ConfigPanel,
    DependentsExplorer,
    ItemOnUseMacrosConfig,
    MidiActivityChoiceDialog,
    MidiRollChoiceDialog,
    ModuleReviewDialog,
    OverTimeEditor,
    ReactionDialog,
    RollModifyDialog,
    RollStatsDisplay,
    SoundConfigPanel,
    TargetConfirmationConfig,
    TargetConfirmationDialog,
    TroubleShooter,
    UndoWorkflow
  },
  logEvaluationData: false
});
for (const appName of ["ConfigPanel", "TargetConfirmationDialog", "TroubleShooter"]) {
  Object.defineProperty(globalThis.MidiQOL, appName, {
    get() {
      foundry.utils.logCompatibilityWarning(
        `MidiQOL.${appName} is deprecated. Use MidiQOL.apps.${appName} instead.`,
        { since: "midi-qol 13.0.50", until: "midi-qol 13.1.0" }
      );
      return globalThis.MidiQOL.apps[appName];
    },
    configurable: true,
    enumerable: true
  });
}
function setupActivities() {
  globalThis.MidiQOL.activityTypes = {};
  setupMidiActivityMixin();
  setupAttackActivity();
  globalThis.MidiQOL.activityTypes["attack"] = { documentClass: MidiAttackActivity };
  setupDamageActivity();
  globalThis.MidiQOL.activityTypes["damage"] = { documentClass: MidiDamageActivity };
  setupCastActivity();
  globalThis.MidiQOL.activityTypes["cast"] = { documentClass: MidiCastActivity };
  setupSaveActivity();
  globalThis.MidiQOL.activityTypes["save"] = { documentClass: MidiSaveActivity };
  setupCheckActivity();
  globalThis.MidiQOL.activityTypes["check"] = { documentClass: MidiCheckActivity };
  setupEnchantActivity();
  globalThis.MidiQOL.activityTypes["enchant"] = { documentClass: MidiEnchantActivity };
  setupHealActivity();
  globalThis.MidiQOL.activityTypes["heal"] = { documentClass: MidiHealActivity };
  setupSummonActivity();
  globalThis.MidiQOL.activityTypes["summon"] = { documentClass: MidiSummonActivity };
  setupTransformActivity();
  globalThis.MidiQOL.activityTypes["transform"] = { documentClass: MidiTransformActivity };
  setupUtilityActivity();
  globalThis.MidiQOL.activityTypes["utility"] = { documentClass: MidiUtilityActivity };
  setupForwardActivity();
  globalThis.MidiQOL.activityTypes["forward"] = { documentClass: MidiForwardActivity };
  globalThis.MidiQOL.activityTypes["MidiActivityMixin"] = MidiActivityMixin;
}
__name(setupActivities, "setupActivities");
Hooks.once("init", async function() {
  GameSystemConfig = CONFIG.DND5E;
  initCoverConstants();
  setupMidiFlagTypes();
  log("Initializing midi-qol");
  window.customElements.define("midi-damage-application", DamageApplicationElementMidi);
  setupActivities();
  if (game.settings.get("midi-qol", "pruneChatLog") && !game.modules.get("chatlog-prune")?.active) {
    foundry.utils.logCompatibilityWarning(
      "midi-qol | Built-in chat log pruning is deprecated. Please install and activate the 'chatlog-prune' module instead.",
      { since: "13.0.37", until: "13.1.0" }
    );
    BaseChatLogClass = CONFIG.ui.chat;
    CONFIG.ui.chat = ChatLogMidi;
  }
  systemConcentrationId = CONFIG.specialStatusEffects.CONCENTRATING;
  Hooks.once("dfreds-convenient-effects.ready", () => {
    setupMidiStatusEffects();
  });
  Hooks.once("dae.ready", (api) => setupMidiFlagEditors(api));
  addConfigOptions();
  GameSystemConfig.areaTargetTypes["emanationNoTemplate"] = {
    label: i18n("midi-qol.emanationNoTemplate"),
    template: "rect",
    standard: true,
    counted: "midi-qol.emanationTemplate.counted"
  };
  GameSystemConfig.damageTypes["none"] = { label: i18n("midi-qol.noType"), icon: `systems/${game.system?.id}/icons/svg/trait-damage-immunities.svg` };
  GameSystemConfig.damageTypes["midi-none"] = { label: i18n("midi-qol.midi-none"), icon: `systems/${game.system?.id}/icons/svg/trait-damage-immunities.svg` };
  GameSystemConfig.damageTypes["vitality"] = { label: i18n("midi-qol.vitalityDamage"), icon: `modules/${MODULE_ID}/icons/vitality.svg` };
  GameSystemConfig.healingTypes["vitality"] = { label: i18n("midi-qol.vitalityHealing"), icon: `modules/${MODULE_ID}/icons/vitality.svg` };
  allAttackTypes = ["rwak", "mwak", "rsak", "msak"];
  midiInitComplete = true;
  if (daeAddAutoFields) {
    setupMidiFlags();
    daeAddAutoFields(midiFlags.filter((f) => !!f.name));
  }
  initHooks();
  registerSettings();
  fetchParams();
  fetchSoundSettings();
  if (game.modules.get("babele")?.active) {
    Hooks.once("babele.ready", MidiSounds.getWeaponBaseTypes);
  } else {
    Hooks.once("ready", MidiSounds.getWeaponBaseTypes);
  }
  preloadTemplates();
  initPatching();
  addDiceTermModifiers();
  globalThis.MidiKeyManager = new MidiKeyManager();
  globalThis.MidiKeyManager.initKeyMappings();
  Hooks.on("error", (...args) => {
    let [message, err] = args;
    TroubleShooter.recordError(err, message);
  });
  gameStats = new RollStats();
});
Hooks.on("dae.modifySpecials", (specKey, specials, _characterSpec) => {
  specials[`flags.${MODULE_ID}.onUseMacroName`] = [new StringField(), CONST.ACTIVE_EFFECT_MODES.CUSTOM];
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.onUseMacroName`);
  specials[`flags.${MODULE_ID}.optional.NAME.macroToCall`] = [new StringField(), CONST.ACTIVE_EFFECT_MODES.CUSTOM];
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.macroToCall`);
  for (let type of ["dm", "da", "di", "dv", "dr"]) {
    specials[`system.traits.${type}.midi.magical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.magical`);
    specials[`system.traits.${type}.midi.non-magical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.non-magical`);
    specials[`system.traits.${type}.midi.non-magical-physical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.non-magical-physical`);
    specials[`system.traits.${type}.midi.non-silver-physical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.non-silver-physical`);
    specials[`system.traits.${type}.midi.non-adamant-physical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.non-adamant-physical`);
    specials[`system.traits.${type}.midi.non-physical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.non-physical`);
    specials[`system.traits.${type}.midi.physical`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.physical`);
    specials[`system.traits.${type}.midi.spell`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.spell`);
    specials[`system.traits.${type}.midi.non-spell`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.${type}.midi.non-spell`);
    if (!["dm", "da"].includes(type)) allAttackTypes.forEach((attackType) => {
      specials[`system.traits.${type}.midi.${attackType}`] = [new StringField(), -1];
      daeFieldBrowserFields.push(`system.traits.${type}.midi.${attackType}`);
    });
  }
  if (GameSystemConfig.damageTypes) {
    specials[`system.traits.dm.midi.all`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.dm.midi.all`);
    Object.keys(GameSystemConfig.damageTypes).forEach((dType) => {
      specials[`system.traits.dm.amount.${dType}`] = [new StringField(), -1];
      daeFieldBrowserFields.push(`system.traits.dm.${dType}`);
    });
    Object.keys(GameSystemConfig.itemActionTypes).forEach((aType) => {
      specials[`system.traits.dm.midi.${aType}`] = [new StringField(), -1];
      daeFieldBrowserFields.push(`system.traits.dm.midi.${aType}`);
    });
    Object.keys(GameSystemConfig.healingTypes).forEach((dType) => {
      specials[`system.traits.dm.amount.${dType}`] = [new StringField(), -1];
      daeFieldBrowserFields.push(`system.traits.dm.midi.${dType}`);
    });
    specials[`system.traits.da.midi.all`] = [new StringField(), -1];
    daeFieldBrowserFields.push(`system.traits.da.midi.all`);
    Object.keys(GameSystemConfig.damageTypes).forEach((dType) => {
      specials[`system.traits.da.${dType}`] = [new StringField(), -1];
      daeFieldBrowserFields.push(`system.traits.da.${dType}`);
    });
    Object.keys(GameSystemConfig.itemActionTypes).forEach((aType) => {
      specials[`system.traits.da.midi.${aType}`] = [new StringField(), -1];
      daeFieldBrowserFields.push(`system.traits.da.midi.${aType}`);
    });
    Object.keys(GameSystemConfig.healingTypes).forEach((dType) => {
      specials[`system.traits.da.${dType}`] = [new StringField(), -1];
      daeFieldBrowserFields[`system.traits.da.${dType}`] = [new StringField(), -1];
    });
  }
  specials[`system.traits.idi.value`] = [new StringField(), -1];
  daeFieldBrowserFields.push(`system.traits.idi.value`);
  specials[`system.traits.idr.value`] = [new StringField(), -1];
  daeFieldBrowserFields.push(`system.traits.idr.value`);
  specials[`system.traits.idv.value`] = [new StringField(), -1];
  daeFieldBrowserFields.push(`system.traits.idv.value`);
  specials[`system.traits.ida.value`] = [new StringField(), -1];
  daeFieldBrowserFields.push(`system.traits.ida.value`);
  specials[`system.traits.idm.value`] = [new StringField(), -1];
  daeFieldBrowserFields.push(`system.traits.idm.value`);
});
Hooks.on("dae.modifyBaseValues", (specKey, baseValues, _characterSpec) => {
  baseValues[`flags.${MODULE_ID}.ActivityOverTime`] = [new StringField({ initial: "" }), -1];
});
Hooks.on("dae.addFieldMappings", (fieldMappings) => {
  GameSystemConfig = CONFIG.DND5E;
  let allAttackTypes3 = ["rwak", "mwak", "rsak", "msak"];
  registerSettings();
  fetchParams();
  for (let key of Object.keys(GameSystemConfig.damageTypes ?? {})) {
    fieldMappings[`flags.${MODULE_ID}.DR.${key}`] = `system.traits.dm.amount.${key}`;
  }
  for (let key of Object.keys(GameSystemConfig.healingTypes ?? {})) {
    fieldMappings[`flags.${MODULE_ID}.DR.${key}`] = `system.traits.dm.amount.${key}`;
  }
  fieldMappings[`flags.${MODULE_ID}.DR.all`] = "system.traits.dm.midi.all";
  Object.keys(GameSystemConfig.itemActionTypes).forEach((aType) => {
    fieldMappings[`flags.${MODULE_ID}.DR.${aType}`] = `system.traits.dm.midi.${aType}`;
  });
  fieldMappings[`flags.${MODULE_ID}.DR.all`] = `system.traits.dm.midi.all`;
  fieldMappings[`flags.${MODULE_ID}.DR.non-magical`] = `system.traits.dm.midi.non-magical`;
  fieldMappings[`flags.${MODULE_ID}.DR.non-magical-physical`] = `system.traits.dm.midi.non-magical-physical`;
  fieldMappings[`flags.${MODULE_ID}.DR.non-silver`] = `system.traits.dm.midi.non-silver-physical`;
  fieldMappings[`flags.${MODULE_ID}.DR.non-adamant`] = `system.traits.dm.midi.non-adamant-physical`;
  fieldMappings[`flags.${MODULE_ID}.DR.non-physical`] = `system.traits.dm.midi.non-physical`;
  fieldMappings[`flags.${MODULE_ID}.DR.non-spell`] = `system.traits.dm.midi.non-spell`;
  fieldMappings[`flags.${MODULE_ID}.DR.spell`] = `system.traits.dm.midi.spell`;
  fieldMappings[`flags.${MODULE_ID}.concentrationSaveBonus`] = "system.attributes.concentration.bonuses.save";
  fieldMappings[`flags.${MODULE_ID}.fail.critical.all`] = `flags.${MODULE_ID}.grants.noCritical.all`;
  for (let attackType of allAttackTypes3) {
    fieldMappings[`flags.${MODULE_ID}.fail.critical.${attackType}`] = `flags.${MODULE_ID}.grants.noCritical.${attackType}`;
  }
  let attackTypes = allAttackTypes3.concat(["save", "check", "skill", "tool"]);
  attackTypes.forEach((attackType) => {
    fieldMappings[`flags.${MODULE_ID}.grants.fail.advantage.attack.${attackType}`] = `flags.${MODULE_ID}.grants.noAdvantage.attack.${attackType}`;
    fieldMappings[`flags.${MODULE_ID}.grants.fail.disadvantage.attack.${attackType}`] = `flags.${MODULE_ID}.grants.noDisadvantage.attack.${attackType}`;
  });
  if (GameSystemConfig.skills) for (let skill of Object.keys(GameSystemConfig.skills)) {
    fieldMappings[`flags.${MODULE_ID}.max.skill.${skill}`] = `system.skills.${skill}.roll.max`;
    fieldMappings[`flags.${MODULE_ID}.min.skill.${skill}`] = `system.skills.${skill}.roll.min`;
  }
  fieldMappings[`flags.${MODULE_ID}.max.ability.save.concentration`] = `system.attributes.concentration.roll.max`;
  fieldMappings[`flags.${MODULE_ID}.min.ability.save.concentration`] = `system.attributes.concentration.roll.min`;
  fieldMappings[`flags.${MODULE_ID}.concentrationSaveBonus`] = `system.attributes.concentration.bonuses.save`;
  fieldMappings[`flags.${MODULE_ID}.sharpShooter`] = `flags.dnd5e.sharpShooter`;
  fieldMappings[`flags.${MODULE_ID}.grants.fail.advantage.attack.all`] = `flags.${MODULE_ID}.grants.noAdvantage.attack.all`;
  fieldMappings[`flags.${MODULE_ID}.grants.fail.disadvantage.attack.all`] = `flags.${MODULE_ID}.grants.noDisadvantage.attack.all`;
  if (debugEnabled > 0) warn("fieldMappings", fieldMappings);
});
Hooks.on("dae.addSpecialDurations", (daeSpecialDurations) => {
  daeSpecialDurations["1Action"] = i18n("dae.1Action");
  daeSpecialDurations["Bonus Action"] = i18n("dae.Bonus Action");
  daeSpecialDurations["Reaction"] = i18n("dae.Reaction");
  daeSpecialDurations["Turn Action"] = i18n("dae.Turn Action");
  daeSpecialDurations["1Spell"] = i18n("dae.1Spell");
  daeSpecialDurations["1Attack"] = i18nFormat2("dae.1Attack", { type: `${i18n("dae.spell")}/${i18n("dae.weapon")} ${i18n("dae.attack")}` });
  daeSpecialDurations["1Hit"] = i18nFormat2("dae.1Hit", { type: `${i18n("dae.spell")}/${i18n("dae.weapon")}` });
  daeSpecialDurations["1Critical"] = i18n("dae.1Critical");
  daeSpecialDurations["1Fumble"] = i18n("dae.1Fumble");
  daeSpecialDurations["1Reaction"] = i18n("dae.1Reaction");
  let attackTypes = ["mwak", "rwak", "msak", "rsak"];
  attackTypes.forEach((at) => {
    daeSpecialDurations[`1Attack:${at}`] = `${GameSystemConfig.itemActionTypes[at]}: ${i18nFormat2("dae.1Attack", { type: GameSystemConfig.itemActionTypes[at] })}`;
    daeSpecialDurations[`1Hit:${at}`] = `${GameSystemConfig.itemActionTypes[at]}: ${i18nFormat2("dae.1Hit", { type: GameSystemConfig.itemActionTypes[at] })}`;
  });
  daeSpecialDurations["DamageDealt"] = i18n("dae.DamageDealt");
  daeSpecialDurations["isAttacked"] = i18n("dae.isAttacked");
  daeSpecialDurations["isDamaged"] = i18n("dae.isDamaged");
  daeSpecialDurations["isHealed"] = i18n("dae.isHealed");
  daeSpecialDurations["zeroHP"] = i18n("dae.ZeroHP");
  daeSpecialDurations["isHit"] = i18n("dae.isHit");
  daeSpecialDurations["isHitCritical"] = i18n("dae.isHitCritical");
  daeSpecialDurations["isSave"] = `${i18n("dae.isRollBase")} ${i18n("dae.isSaveDetail")}`;
  daeSpecialDurations["isSaveSuccess"] = `${i18n("dae.isRollBase")} ${i18n("dae.isSaveDetail")}: ${i18n("dae.success")}`;
  daeSpecialDurations["isSaveFailure"] = `${i18n("dae.isRollBase")} ${i18n("dae.isSaveDetail")}: ${i18n("dae.failure")}`;
  daeSpecialDurations["isConcentrationSave"] = i18n("dae.isConcentrationSave");
  daeSpecialDurations["isConcentrationSaveFail"] = `${i18n("dae.isConcentrationSave")}: ${i18n("dae.failure")}`;
  daeSpecialDurations["isConcentrationSaveSuccess"] = `${i18n("dae.isConcentrationSave")}: ${i18n("dae.success")}`;
  daeSpecialDurations["isCheck"] = `${i18n("dae.isRollBase")} ${i18n("dae.isCheckDetail")}`;
  daeSpecialDurations["isSkill"] = `${i18n("dae.isRollBase")} ${i18n("dae.isSkillDetail")}`;
  daeSpecialDurations["isInitiative"] = `${i18n("dae.isRollBase")} ${i18n("dae.isInitiativeDetail")}`;
  daeSpecialDurations["isMoved"] = i18n("dae.isMoved");
  daeSpecialDurations["longRest"] = i18n("DND5E.LongRest");
  daeSpecialDurations["shortRest"] = i18n("DND5E.ShortRest");
  daeSpecialDurations["newDay"] = `${i18n("DND5E.NewDay")}`;
  Object.keys(GameSystemConfig.abilities).forEach((abl) => {
    let ablString = GameSystemConfig.abilities[abl].label;
    daeSpecialDurations[`isSave.${abl}`] = `${i18n("dae.isRollBase")} ${ablString} ${i18n("dae.isSaveDetail")}`;
    daeSpecialDurations[`isSaveSuccess.${abl}`] = `${i18n("dae.isRollBase")} ${ablString} ${i18n("dae.isSaveDetail")}: ${i18n("dae.success")}`;
    daeSpecialDurations[`isSaveFailure.${abl}`] = `${i18n("dae.isRollBase")} ${ablString} ${i18n("dae.isSaveDetail")}: ${i18n("dae.failure")}`;
    daeSpecialDurations[`isCheck.${abl}`] = `${i18n("dae.isRollBase")} ${ablString} ${i18n("dae.isCheckDetail")}`;
  });
  Object.keys(GameSystemConfig.damageTypes).forEach((key) => {
    daeSpecialDurations[`isDamaged.${key}`] = `${i18n("dae.isDamaged")}: ${GameSystemConfig.damageTypes[key].label}`;
  });
  daeSpecialDurations[`isDamaged.healing`] = `${i18n("dae.isDamaged")}: ${GameSystemConfig.healingTypes["healing"].label}`;
  Object.keys(GameSystemConfig.skills).forEach((skillId) => {
    daeSpecialDurations[`isSkill.${skillId}`] = `${i18n("dae.isRollBase")} ${i18n("dae.isSkillDetail")} ${GameSystemConfig.skills[skillId].label}`;
  });
});
var daeFieldBrowserFields = [];
Hooks.on("dae.setFieldData", (fieldData) => {
  fieldData["MidiQOL"] = Array.from(new Set(daeFieldBrowserFields)).sort();
  log("setDaeFieldData | fieldData", fieldData);
});
Hooks.once("setup", function() {
  setupModules();
  setupSocket();
  fetchParams();
  fetchSoundSettings();
  itemPatching();
  visionPatching();
  initGMActionSetup();
  setupHooks();
  MidiQOL.MQdefaultDamageType = i18n("midi-qol.defaultDamageType");
  MQItemMacroLabel = i18n("midi-qol.ItemMacroText") ?? "ItemMacro";
  MQActivityMacroLabel = i18n("midi-qol.ActivityMacroText") ?? "ActivityMacro";
  setupSheetQol();
  createMidiMacros();
  setupMidiQOLApi();
});
Hooks.once("dae.setupComplete", () => {
  applyPendingLocalizations();
});
Hooks.once("ready", () => {
});
function addConfigOptions() {
  let config = GameSystemConfig ?? {};
  config.damageTypes["none"] = { label: i18n("midi-qol.noType"), icon: "systems/dnd5e/icons/svg/trait-damage-immunities.svg" };
  config.damageTypes["midi-none"] = { label: i18n("midi-qol.midi-none"), icon: "systems/dnd5e/icons/svg/trait-damage-immunities.svg" };
  config.customDamageResistanceTypes = {
    "spell": i18n("midi-qol.SpellDamage"),
    "nonSpell": i18n("midi-qol.NonSpellDamage"),
    "magical": i18n("midi-qol.Magical"),
    "nonMagical": i18n("midi-qol.NonMagical"),
    "physical": i18n("midi-qol.Physical"),
    "nonMagicalPhysical": i18n("midi-qol.NonMagicalPhysical"),
    "nonSilverPhysical": i18n("midi-qol.NonSilverPhysical"),
    "nonAdamantPhysical": i18n("midi-qol.NonAdamantinePhysical")
  };
  config.traits.di.configKey = "damageTypes";
  config.traits.dr.configKey = "damageTypes";
  config.traits.dv.configKey = "damageTypes";
  if (!config.traits.da && game.system?.id === "dnd5e") {
    config.traits.da = {
      labels: { title: "Damage Absorption", localization: "midi-qol.DamageAbsorption" },
      icon: "systems/dnd5e/icons/svg/damageresistances.svg",
      configKey: "damageTypes"
    };
  } else if (config.traits.da) {
    config.traits.da.configKey = "damageTypes";
  }
  const dnd5eReaction = `DND5E.Reaction`;
  globalThis.dnd5e.config.characterFlags["DamageBonusMacro"] = {
    type: String,
    name: "Damage Bonus Macro",
    hint: "Macro to use for damage bonus",
    section: "Midi QOL"
  };
  globalThis.dnd5e.config.characterFlags["initiativeHalfProficiency"] = {
    type: Boolean,
    name: "Half Proficiency for Initiative",
    hint: "add 1/2 proficiency to initiative",
    section: "Midi QOL"
  };
  globalThis.dnd5e.config.characterFlags["initiativeDisadv"] = {
    type: Boolean,
    name: "Disadvantage on Initiative",
    hint: "Provided by fears or magical items",
    section: "Midi QOL"
  };
  globalThis.dnd5e.config.characterFlags["spellSniper"] = {
    type: Boolean,
    name: "Spell Sniper",
    hint: "Provided by feats or magical items",
    section: "Midi QOL"
  };
  globalThis.dnd5e.config.characterFlags["sharpShooter"] = {
    type: Boolean,
    name: "Sharp Shooter",
    hint: "Provided by feats or magical items",
    section: "Midi QOL"
  };
}
__name(addConfigOptions, "addConfigOptions");
Hooks.once("ready", function() {
  cleanCPRFlanked();
  addConfigOptions();
  registerSettings();
  actorAbilityRollPatching();
  setupMidiStatusEffects();
  MQOnUseOptions = {
    "preTargeting": "Called before targeting is resolved (*)",
    "preItemRoll": "Called before the item is rolled (*)",
    "templatePlaced": "Only called once a template is placed",
    "preAttackRoll": "Before Attack Roll",
    "preAttackRollConfig": "Before Attack Configuration",
    "preCheckHits": "Before Check Hits",
    "postAttackRoll": "After Attack Roll",
    "preSave": "Before Save",
    "postSave": "After Save",
    "preDamageRoll": "Before Damage Roll",
    "postDamageRoll": "After Damage Roll",
    "damageBonus": "return a damage bonus",
    "preDamageRollConfig": "Before Damage Configuration",
    "preDamageApplication": "Before Damage Application",
    "preActiveEffects": "Before Active Effects",
    "postActiveEffects": "After Active Effects ",
    "isTargeted": "Target is targeted but before item is rolled",
    "isPreAttacked": "Target is about to be attacked, before reactions",
    "isAttacked": "Target is attacked",
    "isHit": "Target is hit",
    "preTargetSave": "Target is about to roll a saving throw",
    "isSave": "Target rolled a save",
    "isSaveSuccess": "Target rolled a successful save",
    "isSaveFailure": "Target failed a saving throw",
    "preTargetDamageApplication": "Target is about to be damaged by an item",
    "postTargetEffectApplication": "Target has an effect applied by a rolled item",
    "isDamaged": "Target is damaged by an attack",
    "all": "All"
  };
  for (let key of Object.keys(Workflow.stateTable)) {
    const camelKey = `${key.charAt(0).toUpperCase()}${key.slice(1)}`;
    if (MQOnUseOptions[`pre${camelKey}`] === void 0) {
      MQOnUseOptions[`pre${camelKey}`] = `Before state ${camelKey}`;
    } else error2(`pre${camelKey} already exists`);
    if (MQOnUseOptions[`post${camelKey}`] === void 0) {
      MQOnUseOptions[`post${camelKey}`] = `After state ${camelKey}`;
    } else error2(`post${camelKey} already exists`);
  }
  OnUseMacroOptions.setOptions(MQOnUseOptions);
  globalThis.MidiQOL.MQOnUseOptions = MQOnUseOptions;
  MidiSounds.midiSoundsReadyHooks();
  globalThis.dnd5e.config.areaTargetTypes["emanationNoTemplate"] = {
    label: i18n("midi-qol.emanationNoTemplate"),
    template: "rect",
    standard: true,
    counted: "midi-qol.emanationTemplate.counted"
  };
  if (game.user?.isGM && game.settings) {
    const instanceId = game.settings.get(MODULE_ID, "instanceId");
    if ([void 0, ""].includes(instanceId)) {
      game.settings.set(MODULE_ID, "instanceId", foundry.utils.randomID());
    }
    const oldVersion = game.settings.get(MODULE_ID, "last-run-version");
    const newVersion = game.modules.get(MODULE_ID)?.version;
    if (foundry.utils.isNewerVersion(newVersion, oldVersion)) {
      console.warn(`midi-qol | instance ${game.settings.get(MODULE_ID, "instanceId")} version change from ${oldVersion} to ${newVersion}`);
      game.settings.set(MODULE_ID, "last-run-version", newVersion);
    }
    readySettingsSetup();
  }
  Hooks.callAll("midi-qol.ready");
  checkModules();
  readyHooks();
  readyPatching();
  ceInterface = game.modules.get("dfreds-convenient-effects")?.api;
  if (midiSoundSettingsBackup) game.settings.set(MODULE_ID, "MidiSoundSettings-backup", midiSoundSettingsBackup);
  $(document).on("mouseover", ".midi-qol-target-name", (e) => {
    const tokenId = e.currentTarget.id;
    const tokenObj = canvas.tokens?.get(tokenId);
    if (!tokenObj) return;
    tokenObj.hover = true;
  });
  Hooks.callAll("midi-qol.midiReady");
  if (game.user?.isGM) {
    const problems = TroubleShooter.collectTroubleShooterData().problems;
    for (let problem of problems) {
      const message = `midi-qol ${problem.problemSummary} | Open TroubleShooter to fix`;
      if (problem.severity === "Error")
        ui.notifications?.error(message, { permanent: false });
      else console.warn(message);
    }
  }
});
function setupMidiQOLApi() {
  const detectionModes = CONFIG.Canvas.detectionModes;
  const InvisibleDisadvantageVisionModes = Object.keys(detectionModes).filter((dm) => !detectionModes[dm].imprecise);
  const humanoid = ["human", "humanoid", "elven", "elf", "half-elf", "drow", "dwarf", "dwarven", "halfling", "gnome", "tiefling", "orc", "dragonborn", "half-orc"];
  Object.assign(globalThis.MidiQOL, {
    checkRule,
    collectSettingData,
    computeTargetsFromTemplates,
    configSettings: /* @__PURE__ */ __name(() => {
      return configSettings;
    }, "configSettings"),
    debouncedUpdate: throttledUpdate,
    enableWorkflow,
    exportSettingsToJSON,
    fetchParams,
    gameStats,
    getCheckRollModeFor,
    getSaveRollModeFor,
    humanoid,
    importSettingsFromJSON,
    incapacitatedConditions: ["incapacitated", "Convenient Effect: Incapacitated", "stunned", "Convenient Effect: Stunned", "paralyzed", "paralysis", "Convenient Effect: Paralyzed", "unconscious", "Convenient Effect: Unconscious", "dead", "Convenient Effect: Dead", "petrified", "Convenient Effect: Petrified"],
    InvisibleDisadvantageVisionModes,
    midiFlags,
    midiPropertiesOptions: {
      triggeredActivityTargetOptions: Object.entries(triggeredActivityTargetOptions).map(([value, opt]) => {
        return { value, label: i18n(opt) };
      }),
      triggeredActivityRollAsOptions: Object.entries(triggeredActivityRollAsOptions).map(([value, opt]) => {
        return { value, label: i18n(opt) };
      })
    },
    MQOnUseOptions,
    resetConfigSettingsToDefaults: resetConfigSettingsToDefaults2,
    safeGetGameSetting,
    selectTargetsFromTemplates,
    shouldDisplaySaveDC,
    showItemInfo: /* @__PURE__ */ __name((item) => {
      return showItemInfo.bind(item)();
    }, "showItemInfo"),
    socket: /* @__PURE__ */ __name(() => {
      return new SaferSocket(socketlibSocket);
    }, "socket"),
    WallsBlockConditions: ["burrow"]
  });
  Object.defineProperties(globalThis.MidiQOL, {
    currentConfigSettings: {
      get() {
        return configSettings;
      },
      configurable: true,
      enumerable: true
    },
    midiSoundSettings: {
      get() {
        return game.settings.get("midi-qol", "MidiSoundSettings");
      },
      configurable: true,
      enumerable: true
    },
    updatesCache: {
      get() {
        return updatesCache;
      },
      configurable: true,
      enumerable: true
    }
  });
  game.modules.get("midi-qol").api = globalThis.MidiQOL;
  globalThis.MidiDAEEval = {
    testfunc,
    canSee,
    canSense,
    canSenseModes,
    checkIncapacitated,
    checkDistance,
    checkNearby,
    checkActivityRange,
    checkRule,
    computeCoverBonus,
    computeDistance,
    getDistance,
    contestedRoll,
    displayDSNForRoll,
    doConcentrationCheck,
    chooseEffect,
    findNearby,
    findNearbyCount,
    getTraitMult,
    hasCondition,
    hasUsedBonusAction,
    hasUsedReaction,
    humanoid,
    isTargetable: isValidTarget,
    raceOrType,
    typeOrRace,
    safeGetGameSetting,
    setBonusActionUsed,
    setReactionUsed
  };
  globalThis.MidiQOL.actionQueue = new foundry.utils.Semaphore();
  Hooks.callAll("midi-qol.setup", globalThis.MidiQOL);
}
__name(setupMidiQOLApi, "setupMidiQOLApi");
function testfunc(...args) {
  console.warn("MidiQOL testfunc called ", ...args);
}
__name(testfunc, "testfunc");
async function moveToken(tokenRef, newCenter, options, legacyOptions) {
  let resolvedOptions;
  if (typeof options === "boolean") {
    foundry.utils.logCompatibilityWarning("MidiQOL.moveToken: passing 'animate' as a boolean third argument is deprecated. Use options: { animate: false } or { teleport: true } instead.", { since: "13.0.56", until: "14.0.0" });
    resolvedOptions = { ...legacyOptions, teleport: !options ? true : legacyOptions?.teleport ?? false };
  } else {
    resolvedOptions = options;
  }
  const tokenUuid = getTokenDocument(tokenRef)?.uuid;
  if (tokenUuid) return unTimedExecuteAsGM("moveToken", { tokenUuid, newCenter, options: resolvedOptions });
}
__name(moveToken, "moveToken");
async function moveTokenAwayFromPoint(targetRef, distance, point, options, legacyCheckCollision, legacyOptions) {
  let resolvedOptions;
  if (typeof options === "boolean") {
    foundry.utils.logCompatibilityWarning("MidiQOL.moveTokenAwayFromPoint: passing 'animate' and 'checkCollision' as positional arguments is deprecated. Use options: { animate, ignoreWalls, ignoreTokens } instead.", { since: "13.0.56", until: "14.0.0" });
    const animate = options;
    const checkCollision = legacyCheckCollision ?? false;
    resolvedOptions = {
      ...legacyOptions,
      animate: legacyOptions?.animate ?? animate,
      teleport: !animate ? true : legacyOptions?.teleport ?? false,
      ignoreWalls: legacyOptions?.ignoreWalls ?? !checkCollision,
      ignoreTokens: legacyOptions?.ignoreTokens ?? !checkCollision
    };
  } else {
    resolvedOptions = options;
  }
  const targetUuid = getTokenDocument(targetRef)?.uuid;
  if (point && targetUuid && distance)
    return unTimedExecuteAsGM("moveTokenAwayFromPoint", { targetUuid, distance, point, options: resolvedOptions });
}
__name(moveTokenAwayFromPoint, "moveTokenAwayFromPoint");
async function teleportToken(tokenRef, destination) {
  const tokenUuid = getTokenDocument(tokenRef)?.uuid;
  if (tokenUuid) return unTimedExecuteAsGM("teleportToken", { tokenUuid, destination });
}
__name(teleportToken, "teleportToken");
async function createEffects2(data) {
  const { actorUuid, effects, options = { keepId: true } } = data;
  if (!actorUuid) {
    error2("createEffects failed. Missing actorUuid");
    return false;
  }
  if (effects?.length)
    return unTimedExecuteAsGM("createEffects", { actorUuid, effects, options });
  else {
    error2("createEffects failed with missing data, which should include {actorUuid, effects: [], options: {}} but got", data);
    return false;
  }
}
__name(createEffects2, "createEffects");
async function removeEffects2(data) {
  if (!data.actorUuid) {
    error2("createEffects failed. Missing actorUuid");
    return false;
  }
  ;
  if (!data.effects.length) {
    error2("removeEffects failed with missing data, which should include {actorUuid, effects: [<effectIDs>],} but got", data);
    return false;
  }
  const statusEffectIds = new Set(CONFIG.statusEffects.map((statusEffect) => statusEffect.id));
  for (let i = 0; i < data.effects.length; i++) {
    const statusId = data.effects[i];
    if (statusEffectIds.has(statusId)) {
      data.effects[i] = getStaticID(statusId);
    }
  }
  return unTimedExecuteAsGM("removeEffects", data);
}
__name(removeEffects2, "removeEffects");
async function updateEffects2(data) {
  if (data.actorUuid && data.updates?.length)
    return unTimedExecuteAsGM("updateEffects", data);
  else {
    error2("updateEffects failed missing data which should include {actorUuid, updates: []} but got", data);
    return false;
  }
}
__name(updateEffects2, "updateEffects");
var pendingLocalizations = [];
function registerFlagLocalization(flagPath, templateKey, label) {
  pendingLocalizations.push({ flagPath, templateKey, label });
}
__name(registerFlagLocalization, "registerFlagLocalization");
function applyPendingLocalizations() {
  const lm = globalThis.DAE?.localizationMap;
  if (!lm || !game.i18n) return;
  for (const { flagPath, templateKey, label } of pendingLocalizations) {
    const resolvedLabel = typeof label === "function" ? label() : label;
    lm[flagPath] = {
      name: i18nFormat2(`midi-qol.flagTemplate.${templateKey}.name`, { label: resolvedLabel }),
      description: i18nFormat2(`midi-qol.flagTemplate.${templateKey}.description`, { label: resolvedLabel })
    };
  }
  pendingLocalizations = [];
}
__name(applyPendingLocalizations, "applyPendingLocalizations");
function setupMidiFlags() {
  const booleanFormula = /* @__PURE__ */ __name((flag) => midiFlags.push({ name: flag, type: daeBooleanFormulaField }), "booleanFormula");
  const untyped = /* @__PURE__ */ __name((flag) => midiFlags.push({ name: flag }), "untyped");
  booleanFormula(`flags.${MODULE_ID}.advantage.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.all`);
  booleanFormula(`flags.${MODULE_ID}.disadvantage.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.all`);
  booleanFormula(`flags.${MODULE_ID}.fail.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.all`);
  booleanFormula(`flags.${MODULE_ID}.success.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.all`);
  booleanFormula(`flags.${MODULE_ID}.neverTarget`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.neverTarget`);
  untyped(`flags.${MODULE_ID}.grants.critical.range`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.critical.range`);
  untyped(`flags.${MODULE_ID}.grants.criticalThreshold`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.criticalThreshold`);
  booleanFormula(`flags.${MODULE_ID}.advantage.concentration`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.concentration`);
  booleanFormula(`flags.${MODULE_ID}.disadvantage.concentration`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.concentration`);
  booleanFormula(`flags.${MODULE_ID}.noAdvantage.concentration`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.concentration`);
  booleanFormula(`flags.${MODULE_ID}.noDisadvantage.concentration`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.concentration`);
  booleanFormula(`flags.${MODULE_ID}.noAdvantage.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.all`);
  booleanFormula(`flags.${MODULE_ID}.noDisadvantage.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.all`);
  booleanFormula(`flags.${MODULE_ID}.initiativeAdv`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.initiativeAdv`);
  booleanFormula(`flags.${MODULE_ID}.initiativeDisadv`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.initiativeDisadv`);
  booleanFormula(`flags.${MODULE_ID}.initiativeNoAdv`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.initiativeNoAdv`);
  booleanFormula(`flags.${MODULE_ID}.initiativeNoDisadv`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.initiativeNoDisadv`);
  booleanFormula(`flags.${MODULE_ID}.ignoreNearbyFoes`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.ignoreNearbyFoes`);
  untyped(`flags.${MODULE_ID}.bloodsense`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.bloodsense`);
  untyped(`flags.${MODULE_ID}.`);
  untyped(`flags.${MODULE_ID}.concentrationSaveBonus`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.concentrationSaveBonus`);
  booleanFormula(`flags.${MODULE_ID}.potentCantrip`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.potentCantrip`);
  booleanFormula(`flags.${MODULE_ID}.sculptSpells`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.sculptSpells`);
  booleanFormula(`flags.${MODULE_ID}.carefulSpells`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.carefulSpells`);
  booleanFormula(`flags.${MODULE_ID}.magicResistance.save.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.magicResistance.save.all`);
  booleanFormula(`flags.${MODULE_ID}.magicResistance.check.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.magicResistance.check.all`);
  booleanFormula(`flags.${MODULE_ID}.magicResistance.skill.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.magicResistance.skill.all`);
  const extendedAttackTypeLabels = { check: "Ability Check", skill: "Skill Check", tool: "Tool Check", all: "All" };
  let attackTypes = allAttackTypes.concat(["save", "check", "skill", "tool", "all"]);
  attackTypes.forEach((at) => {
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.attack.${at}`);
    const label = /* @__PURE__ */ __name(() => (GameSystemConfig.itemActionTypes[at] ?? extendedAttackTypeLabels[at] ?? at).replace(/ Attack$/, ""), "label");
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.attack.${at}`, "grants.noAdvantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.attack.${at}`, "grants.disadvantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.attack.${at}`, "grants.noDisadvantage.attack", label);
  });
  attackTypes = allAttackTypes.concat(["heal", "other", "save", "util", "all"]);
  attackTypes.forEach((at) => {
    untyped(`flags.${MODULE_ID}.range.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.range.${at}`);
    untyped(`flags.${MODULE_ID}.long.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.long.${at}`);
    booleanFormula(`flags.${MODULE_ID}.advantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.fail.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.success.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.critical.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.critical.${at}`);
    booleanFormula(`flags.${MODULE_ID}.noCritical.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noCritical.${at}`);
    booleanFormula(`flags.${MODULE_ID}.noFumble.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noFumble.${at}`);
    booleanFormula(`flags.${MODULE_ID}.fumble.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fumble.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.attack.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.critical.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.critical.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noCritical.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noCritical.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noFumble.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noFumble.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.fumble.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.fumble.${at}`);
    booleanFormula(`flags.${MODULE_ID}.fail.critical.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.critical.${at}`);
    untyped(`flags.${MODULE_ID}.grants.attack.bonus.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.attack.bonus.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.attack.success.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.attack.success.${at}`);
    booleanFormula(`flags.${MODULE_ID}.grants.attack.fail.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.attack.fail.${at}`);
    if (at !== "heal") untyped(`flags.${MODULE_ID}.DR.${at}`);
    booleanFormula(`flags.${MODULE_ID}.advantage.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.damage.${at}`);
    untyped(`flags.${MODULE_ID}.max.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.max.damage.${at}`);
    untyped(`flags.${MODULE_ID}.min.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.min.damage.${at}`);
    untyped(`flags.${MODULE_ID}.grants.max.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.max.damage.${at}`);
    untyped(`flags.${MODULE_ID}.grants.min.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.min.damage.${at}`);
    untyped(`flags.${MODULE_ID}.grants.bonus.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.bonus.damage.${at}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.attack.${at}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.attack.fail.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.attack.fail.${at}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.damage.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.damage.${at}`);
    untyped(`flags.${MODULE_ID}.rangeOverride.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.rangeOverride.attack.${at}`);
    const label = /* @__PURE__ */ __name(() => (GameSystemConfig.itemActionTypes[at] ?? extendedAttackTypeLabels[at] ?? at).replace(/ Attack$/, ""), "label");
    registerFlagLocalization(`flags.${MODULE_ID}.range.${at}`, "range", label);
    registerFlagLocalization(`flags.${MODULE_ID}.long.${at}`, "long", label);
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.attack.${at}`, "advantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.attack.${at}`, "disadvantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.attack.${at}`, "noAdvantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.attack.${at}`, "noDisadvantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.attack.${at}`, "fail.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.success.attack.${at}`, "success.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.critical.${at}`, "critical", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noCritical.${at}`, "noCritical", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noFumble.${at}`, "noFumble", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fumble.${at}`, "fumble", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.attack.${at}`, "grants.advantage.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.critical.${at}`, "grants.critical", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noCritical.${at}`, "grants.noCritical", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noFumble.${at}`, "grants.noFumble", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.fumble.${at}`, "grants.fumble", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.critical.${at}`, "fail.critical", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.attack.bonus.${at}`, "grants.attack.bonus", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.attack.success.${at}`, "grants.attack.success", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.attack.fail.${at}`, "grants.attack.fail", label);
    if (at !== "heal") registerFlagLocalization(`flags.${MODULE_ID}.DR.${at}`, "DR.attackType", label);
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.damage.${at}`, "advantage.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.max.damage.${at}`, "max.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.min.damage.${at}`, "min.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.max.damage.${at}`, "grants.max.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.min.damage.${at}`, "grants.min.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.bonus.damage.${at}`, "grants.bonus.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.attack.${at}`, "optional.NAME.attack", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.attack.fail.${at}`, "optional.NAME.attack.fail", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.damage.${at}`, "optional.NAME.damage", label);
    registerFlagLocalization(`flags.${MODULE_ID}.rangeOverride.attack.${at}`, "rangeOverride.attack", label);
  });
  booleanFormula(`flags.${MODULE_ID}.advantage.ability.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.ability.all`);
  booleanFormula(`flags.${MODULE_ID}.disadvantage.ability.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.ability.all`);
  booleanFormula(`flags.${MODULE_ID}.fail.ability.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.ability.all`);
  booleanFormula(`flags.${MODULE_ID}.success.ability.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.ability.all`);
  untyped(`flags.${MODULE_ID}.max.ability.save.concentration`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.max.ability.save.concentration`);
  untyped(`flags.${MODULE_ID}.min.ability.save.concentration`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.min.ability.save.concentration`);
  booleanFormula(`flags.${MODULE_ID}.sharpShooter`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.sharpShooter`);
  untyped(`flags.${MODULE_ID}.rollModifiers.damage.all.all`);
  Object.keys(GameSystemConfig.damageTypes).forEach((dt) => {
    untyped(`flags.${MODULE_ID}.rollModifiers.damage.all.${dt}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.damage.all.${dt}`);
  });
  untyped(`flags.${MODULE_ID}.rollModifiers.attack.all`);
  attackTypes.forEach((at) => {
    untyped(`flags.${MODULE_ID}.rollModifiers.attack.${at}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.attack.${at}`);
    untyped(`flags.${MODULE_ID}.rollModifiers.damage.${at}.all`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.damage.${at}.all`);
    Object.keys(GameSystemConfig.damageTypes).forEach((dt) => {
      untyped(`flags.${MODULE_ID}.rollModifiers.damage.${at}.${dt}`);
      daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.damage.${at}.${dt}`);
    });
  });
  ["weapon", "spell", "tool", "feat"].forEach((it) => {
    untyped(`flags.${MODULE_ID}.rollModifiers.attack.${it}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.attack.${it}`);
    untyped(`flags.${MODULE_ID}.rollModifiers.damage.${it}.all`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.damage.${it}.all`);
    Object.keys(GameSystemConfig.damageTypes).forEach((dt) => {
      untyped(`flags.${MODULE_ID}.rollModifiers.damage.${it}.${dt}`);
      daeFieldBrowserFields.push(`flags.${MODULE_ID}.rollModifiers.damage.${it}.${dt}`);
    });
  });
  untyped(`flags.${MODULE_ID}.actions.reactionsMax`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.actions.reactionsMax`);
  untyped(`flags.${MODULE_ID}.actions.reactionsReset`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.actions.reactionsReset`);
  untyped(`flags.${MODULE_ID}.actions.bonusActionsMax`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.actions.bonusActionsMax`);
  untyped(`flags.${MODULE_ID}.actions.bonusActionsReset`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.actions.bonusActionsReset`);
  Object.keys(GameSystemConfig.abilities).concat(["all"]).forEach((abl) => {
    booleanFormula(`flags.${MODULE_ID}.advantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.advantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.fail.ability.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.ability.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.fail.ability.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.ability.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.success.ability.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.ability.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.success.ability.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.ability.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.superSaver.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.superSaver.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.semiSuperSaver.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.semiSuperSaver.${abl}`);
    untyped(`flags.${MODULE_ID}.max.ability.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.max.ability.save.${abl}`);
    untyped(`flags.${MODULE_ID}.min.ability.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.min.ability.save.${abl}`);
    untyped(`flags.${MODULE_ID}.max.ability.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.max.ability.check.${abl}`);
    untyped(`flags.${MODULE_ID}.min.ability.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.min.ability.check.${abl}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.save.${abl}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.save.fail.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.save.fail.${abl}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.check.${abl}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.check.fail.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.check.fail.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.magicResistance.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.magicResistance.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.magicVulnerability.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.magicVulnerability.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.save.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.save.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.check.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.check.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.advantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.skill.${abl}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.skill.${abl}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.skill.${abl}`);
    const label = /* @__PURE__ */ __name(() => GameSystemConfig.abilities[abl]?.label ?? (abl === "all" ? "All" : abl), "label");
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.check.${abl}`, "advantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.check.${abl}`, "disadvantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.save.${abl}`, "advantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.save.${abl}`, "disadvantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.check.${abl}`, "noAdvantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.save.${abl}`, "noAdvantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.check.${abl}`, "noDisadvantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.save.${abl}`, "noDisadvantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.ability.check.${abl}`, "fail.ability.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.ability.save.${abl}`, "fail.ability.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.success.ability.check.${abl}`, "success.ability.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.success.ability.save.${abl}`, "success.ability.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.superSaver.${abl}`, "superSaver", label);
    registerFlagLocalization(`flags.${MODULE_ID}.semiSuperSaver.${abl}`, "semiSuperSaver", label);
    registerFlagLocalization(`flags.${MODULE_ID}.max.ability.save.${abl}`, "max.ability.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.min.ability.save.${abl}`, "min.ability.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.max.ability.check.${abl}`, "max.ability.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.min.ability.check.${abl}`, "min.ability.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.save.${abl}`, "optional.NAME.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.save.fail.${abl}`, "optional.NAME.save.fail", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.check.${abl}`, "optional.NAME.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.check.fail.${abl}`, "optional.NAME.check.fail", label);
    registerFlagLocalization(`flags.${MODULE_ID}.magicResistance.${abl}`, "magicResistance", label);
    registerFlagLocalization(`flags.${MODULE_ID}.magicVulnerability.${abl}`, "magicVulnerability", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.save.${abl}`, "grants.advantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.check.${abl}`, "grants.advantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.skill.${abl}`, "grants.advantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.save.${abl}`, "grants.disadvantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.check.${abl}`, "grants.disadvantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.skill.${abl}`, "grants.disadvantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.save.${abl}`, "grants.noAdvantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.save.${abl}`, "grants.noDisadvantage.save", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.check.${abl}`, "grants.noAdvantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.check.${abl}`, "grants.noDisadvantage.check", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.skill.${abl}`, "grants.noAdvantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.skill.${abl}`, "grants.noDisadvantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.skill.${abl}`, "advantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.skill.${abl}`, "disadvantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.skill.${abl}`, "noAdvantage.abilitySkill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.skill.${abl}`, "noDisadvantage.abilitySkill", label);
  });
  Object.keys(GameSystemConfig.spellSchools).forEach((school) => {
    booleanFormula(`flags.${MODULE_ID}.advantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.fail.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.success.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.save.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.save.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.save.school.${school}`);
    const label = /* @__PURE__ */ __name(() => GameSystemConfig.spellSchools[school]?.label ?? school, "label");
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.save.school.${school}`, "advantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.save.school.${school}`, "disadvantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.save.school.${school}`, "noAdvantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.save.school.${school}`, "noDisadvantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.save.school.${school}`, "fail.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.success.save.school.${school}`, "success.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.save.school.${school}`, "grants.advantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.save.school.${school}`, "grants.disadvantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.save.school.${school}`, "grants.noAdvantage.save.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.save.school.${school}`, "grants.noDisadvantage.save.school", label);
    booleanFormula(`flags.${MODULE_ID}.advantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.attack.school.${school}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.attack.school.${school}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.attack.school.${school}`);
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.attack.school.${school}`, "advantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.attack.school.${school}`, "disadvantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.attack.school.${school}`, "noAdvantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.attack.school.${school}`, "noDisadvantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.attack.school.${school}`, "grants.advantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.attack.school.${school}`, "grants.disadvantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.attack.school.${school}`, "grants.noAdvantage.attack.school", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.attack.school.${school}`, "grants.noDisadvantage.attack.school", label);
  });
  untyped(`flags.${MODULE_ID}.max.skill.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.max.skill.all`);
  untyped(`flags.${MODULE_ID}.min.skill.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.min.skill.all`);
  Object.keys(GameSystemConfig.skills).concat(["all"]).forEach((skill) => {
    booleanFormula(`flags.${MODULE_ID}.advantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.fail.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.success.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.skill.${skill}`);
    untyped(`flags.${MODULE_ID}.optional.NAME.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.skill.${skill}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.skill.${skill}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.skill.${skill}`);
    const label = /* @__PURE__ */ __name(() => GameSystemConfig.skills[skill]?.label ?? (skill === "all" ? "All" : skill), "label");
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.skill.${skill}`, "advantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.skill.${skill}`, "disadvantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.skill.${skill}`, "noAdvantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.skill.${skill}`, "noDisadvantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.skill.${skill}`, "fail.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.success.skill.${skill}`, "success.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.optional.NAME.skill.${skill}`, "optional.NAME.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.skill.${skill}`, "grants.advantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.skill.${skill}`, "grants.disadvantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.skill.${skill}`, "grants.noAdvantage.skill", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.skill.${skill}`, "grants.noDisadvantage.skill", label);
  });
  booleanFormula(`flags.${MODULE_ID}.advantage.deathSave`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.deathSave`);
  booleanFormula(`flags.${MODULE_ID}.disadvantage.deathSave`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.deathSave`);
  booleanFormula(`flags.${MODULE_ID}.noAdvantage.deathSave`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.deathSave`);
  booleanFormula(`flags.${MODULE_ID}.noDisadvantage.deathSave`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.deathSave`);
  untyped(`flags.${MODULE_ID}.deathSaveBonus`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.deathSaveBonus`);
  booleanFormula(`flags.${MODULE_ID}.fail.deathSave`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.deathSave`);
  booleanFormula(`flags.${MODULE_ID}.success.deathSave`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.deathSave`);
  Object.keys(GameSystemConfig.tools).concat(["all"]).forEach((tool) => {
    booleanFormula(`flags.${MODULE_ID}.advantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.advantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.disadvantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.disadvantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.noAdvantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noAdvantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.noDisadvantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.noDisadvantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.fail.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.success.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.success.tool.${tool}`);
    untyped(`flags.${MODULE_ID}.max.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.max.tool.${tool}`);
    untyped(`flags.${MODULE_ID}.min.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.min.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.grants.advantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.advantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.grants.disadvantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.disadvantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noAdvantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noAdvantage.tool.${tool}`);
    booleanFormula(`flags.${MODULE_ID}.grants.noDisadvantage.tool.${tool}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.grants.noDisadvantage.tool.${tool}`);
    const label = /* @__PURE__ */ __name(() => tool === "all" ? "All" : globalThis.dnd5e?.documents.Trait.keyLabel(`tool:${tool}`) ?? tool, "label");
    registerFlagLocalization(`flags.${MODULE_ID}.advantage.tool.${tool}`, "advantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.disadvantage.tool.${tool}`, "disadvantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noAdvantage.tool.${tool}`, "noAdvantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.noDisadvantage.tool.${tool}`, "noDisadvantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.fail.tool.${tool}`, "fail.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.success.tool.${tool}`, "success.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.max.tool.${tool}`, "max.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.min.tool.${tool}`, "min.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.advantage.tool.${tool}`, "grants.advantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.disadvantage.tool.${tool}`, "grants.disadvantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noAdvantage.tool.${tool}`, "grants.noAdvantage.tool", label);
    registerFlagLocalization(`flags.${MODULE_ID}.grants.noDisadvantage.tool.${tool}`, "grants.noDisadvantage.tool", label);
  });
  ["vocal", "somatic", "material"].forEach((comp) => {
    booleanFormula(`flags.${MODULE_ID}.fail.spell.${comp.toLowerCase()}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.spell.${comp.toLowerCase()}`);
  });
  untyped(`flags.${MODULE_ID}.DR.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.all`);
  untyped(`flags.${MODULE_ID}.DR.non-magical`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.non-magical`);
  untyped(`flags.${MODULE_ID}.DR.non-magical-physical`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.non-magical-physical`);
  untyped(`flags.${MODULE_ID}.DR.non-silver`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.non-silver`);
  untyped(`flags.${MODULE_ID}.DR.non-adamant`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.non-adamant`);
  untyped(`flags.${MODULE_ID}.DR.non-physical`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.non-physical`);
  untyped(`flags.${MODULE_ID}.DR.final`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.final`);
  untyped(`flags.${MODULE_ID}.damage.reroll-kh`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.damage.reroll-kh`);
  untyped(`flags.${MODULE_ID}.damage.reroll-kl`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.damage.reroll-kl`);
  Object.keys(GameSystemConfig.damageTypes).forEach((key) => {
    untyped(`flags.${MODULE_ID}.DR.${key}`);
    daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.${key}`);
    const label = /* @__PURE__ */ __name(() => GameSystemConfig.damageTypes[key]?.label ?? key, "label");
    registerFlagLocalization(`flags.${MODULE_ID}.DR.${key}`, "DR.damageType", label);
  });
  untyped(`flags.${MODULE_ID}.DR.healing`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.healing`);
  untyped(`flags.${MODULE_ID}.DR.temphp`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.DR.temphp`);
  untyped(`flags.${MODULE_ID}.optional.NAME.displayBonusRolls`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.displayChatCard`);
  untyped(`flags.${MODULE_ID}.optional.NAME.label`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.label`);
  untyped(`flags.${MODULE_ID}.optional.NAME.skill.fail.all`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.skill.fail.all`);
  untyped(`flags.${MODULE_ID}.optional.NAME.count`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.count`);
  untyped(`flags.${MODULE_ID}.optional.NAME.countAlt`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.countAlt`);
  untyped(`flags.${MODULE_ID}.optional.NAME.ac`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.ac`);
  untyped(`flags.${MODULE_ID}.optional.NAME.criticalDamage`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.criticalDamage`);
  untyped(`flags.${MODULE_ID}.optional.NAME.activation`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.activation`);
  untyped(`flags.${MODULE_ID}.optional.NAME.force`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.optional.NAME.force`);
  booleanFormula(`flags.${MODULE_ID}.uncanny-dodge`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.uncanny-dodge`);
  untyped(`flags.${MODULE_ID}.OverTime`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.OverTime`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.ActivityOverTime`);
  booleanFormula(`flags.${MODULE_ID}.inMotion`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.inMotion`);
  booleanFormula(`flags.${MODULE_ID}.fail.disadvantage.heavy`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.fail.disadvantage.heavy`);
  booleanFormula(`flags.${MODULE_ID}.canFlank`);
  daeFieldBrowserFields.push(`flags.${MODULE_ID}.canFlank`);
}
__name(setupMidiFlags, "setupMidiFlags");
function setupMidiFlagEditors(DAEapi) {
  if (DAEapi.registerFieldEditor) {
    DAEapi.registerFieldEditor({
      keyMatch: `flags.${MODULE_ID}.OverTime`,
      editor: /* @__PURE__ */ __name(async (currentValue, context) => {
        return new Promise((resolve) => {
          new OverTimeEditor({
            value: currentValue,
            effect: context.effect,
            parent: context.parent,
            onSubmit: /* @__PURE__ */ __name((newValue) => resolve(newValue), "onSubmit"),
            onCancel: /* @__PURE__ */ __name(() => resolve(null), "onCancel")
          }).render({ force: true });
        });
      }, "editor"),
      icon: "fas fa-clock",
      tooltip: i18n("midi-qol.OverTimeEditor.Tooltip")
    });
    const macroAutoComplete = game.modules?.get("macro-autocomplete");
    if (macroAutoComplete?.active) {
      const conditionFlagPrefixes = [
        `flags.${MODULE_ID}.advantage.`,
        `flags.${MODULE_ID}.disadvantage.`,
        `flags.${MODULE_ID}.noAdvantage.`,
        `flags.${MODULE_ID}.noDisadvantage.`,
        `flags.${MODULE_ID}.grants.advantage.`,
        `flags.${MODULE_ID}.grants.disadvantage.`,
        `flags.${MODULE_ID}.grants.noAdvantage.`,
        `flags.${MODULE_ID}.grants.noDisadvantage.`,
        `flags.${MODULE_ID}.fail.`,
        `flags.${MODULE_ID}.success.`,
        `flags.${MODULE_ID}.max.`,
        `flags.${MODULE_ID}.min.`,
        `flags.${MODULE_ID}.optional.`
      ];
      for (const prefix of conditionFlagPrefixes) {
        DAEapi.registerFieldEditor({
          keyMatch: prefix,
          editor: /* @__PURE__ */ __name(async (currentValue, context) => {
            return new Promise((resolve) => {
              new ConditionEditor({
                value: currentValue,
                key: context.key,
                effect: context.effect,
                parent: context.parent,
                onSubmit: /* @__PURE__ */ __name((newValue) => resolve(newValue), "onSubmit"),
                onCancel: /* @__PURE__ */ __name(() => resolve(null), "onCancel")
              }).render({ force: true });
            });
          }, "editor"),
          icon: "fas fa-code",
          tooltip: i18n("midi-qol.ConditionEditor.Tooltip")
        });
      }
    }
  }
}
__name(setupMidiFlagEditors, "setupMidiFlagEditors");
var MQMacros = [
  {
    name: "MidiQOL.showTroubleShooter",
    checkVersion: true,
    version: "11.0.9",
    permission: { default: 1 },
    commandText: `
    new MidiQOL.TroubleShooter().render(true)`
  },
  {
    name: "MidiQOL.exportTroubleShooterData",
    checkVersion: true,
    version: "11.0.9.1",
    permission: { default: 1 },
    commandText: `MidiQOL.TroubleShooter.exportTroubleShooterData()`
  },
  {
    name: "MidiQOL.GMShowPlayerDamageCards",
    checkVersion: true,
    version: "11.4.10",
    commandText: `
    const matches = document.querySelectorAll(".midi-qol-player-damage-card");
    matches.forEach(element => {
      let target = element.parentElement.parentElement.parentElement;
      target.style.display = "inherit";
    })`
  }
];
async function createMidiMacros() {
  const midiVersion = "11.0.9";
  if (game.user?.isGM) {
    for (let macroSpec of MQMacros) {
      try {
        let existingMacros = game.macros?.filter((m) => m.name === macroSpec.name) ?? [];
        if (existingMacros.length > 0) {
          for (let macro of existingMacros) {
            if (macroSpec.checkVersion && !foundry.utils.isNewerVersion(macroSpec.version, macro.flags["midi-version"] ?? "0.0.0"))
              continue;
            await macro.update({ command: macroSpec.commandText, "flags.midi-version": macroSpec.version }, {});
          }
        } else {
          const macroData = {
            _id: null,
            name: macroSpec.name,
            type: "script",
            author: game.user?.id,
            img: "icons/svg/dice-target.svg",
            scope: "global",
            command: macroSpec.commandText,
            folder: null,
            sort: 0,
            permission: {
              default: 1
            },
            flags: { "midi-version": macroSpec.version ?? "midiVersion" }
          };
          await Macro.createDocuments([macroData]);
          log(`Macro ${macroData.name} created`);
        }
      } catch (err) {
        const message = `createMidiMacros | failed to create macro ${macroSpec.name}`;
        TroubleShooter.recordError(err, message);
        error2(err, message);
      }
    }
  }
}
__name(createMidiMacros, "createMidiMacros");
function setupMidiStatusEffects() {
  systemConcentrationId = CONFIG.specialStatusEffects.CONCENTRATING;
  if (!CONFIG.statusEffects.find((e) => e.id === systemConcentrationId)) {
    CONFIG.statusEffects.push({ id: systemConcentrationId, name: i18n("EFFECT.DND5E.StatusConcentrating"), img: "systems/dnd5e/icons/svg/statuses/concentrating.svg", special: "CONCENTRATING" });
  }
  if (configSettings.enforceBonusActions !== "none") {
    if (!CONFIG.statusEffects.find((e) => e._id === getStaticID("bonusaction"))) {
      CONFIG.statusEffects.push({
        id: "bonusaction",
        _id: getStaticID("bonusaction"),
        name: i18n("midi-qol.bonusActionUsed"),
        changes: [
          { key: "flags.midi-qol.actions.bonus", mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM, value: "true" },
          { key: "flags.midi-qol.actions.bonusActionsUsed", mode: CONST.ACTIVE_EFFECT_MODES.ADD, value: "1", priority: 20 }
        ],
        img: "modules/midi-qol/icons/bonus-action.svg",
        flags: { dae: { specialDuration: ["turnStart", "combatEnd", "shortRest"] } }
      });
    }
    ActiveEffect.implementation.fromStatusEffect("bonusaction").then((effect) => {
      midiBonusActionEffect = effect;
      globalThis.MidiQOL.midiBonusActionEffect = effect;
    });
  }
  if (configSettings.enforceReactions !== "none") {
    if (!CONFIG.statusEffects.find((e) => e._id === getStaticID("reaction"))) {
      CONFIG.statusEffects.push({
        id: "reaction",
        _id: getStaticID("reaction"),
        name: i18n("midi-qol.reactionUsed"),
        changes: [
          { key: "flags.midi-qol.actions.reaction", mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM, value: "true" },
          { key: "flags.midi-qol.actions.reactionsUsed", mode: CONST.ACTIVE_EFFECT_MODES.ADD, value: "1", priority: 20 }
        ],
        img: "modules/midi-qol/icons/reaction.svg",
        // effectData: { transfer: false },
        flags: { dae: { specialDuration: ["turnStart", "combatEnd", "shortRest"] } }
      });
    }
    ActiveEffect.implementation.fromStatusEffect("reaction").then((effect) => {
      midiReactionEffect = effect;
      globalThis.MidiQOL.midiReactionEffect = effect;
    });
  }
  if (!CONFIG.statusEffects.find((e) => e._id === getStaticID("flanking"))) {
    CONFIG.statusEffects.push({
      id: "flanking",
      _id: getStaticID("flanking"),
      name: i18n("midi-qol.Flanking"),
      changes: getFlankingStatusChanges(),
      img: "icons/svg/sword.svg",
      // effectData: { transfer: false },
      flags: { dae: { specialDuration: ["combatEnd"] } }
    });
  }
  ActiveEffect.implementation.fromStatusEffect("flanking").then((effect) => {
    midiFlankingEffect = effect;
    globalThis.MidiQOL.midiFlankingEffect = effect;
  });
  if (!CONFIG.statusEffects.find((e) => e._id === getStaticID("flanked"))) {
    CONFIG.statusEffects.push({
      id: "flanked",
      _id: getStaticID("flanked"),
      name: i18n("midi-qol.Flanked"),
      changes: getFlankedStatusChanges(),
      img: "modules/midi-qol/icons/encirclement.svg",
      // effectData: { transfer: false },
      flags: { dae: { specialDuration: ["combatEnd"] } }
    });
  }
  ;
  ActiveEffect.implementation.fromStatusEffect("flanked").then((effect) => {
    midiFlankedEffect = effect;
    globalThis.MidiQOL.midiFlankedEffect = effect;
  });
}
__name(setupMidiStatusEffects, "setupMidiStatusEffects");
function getFlankingStatusChanges() {
  const bonus = configSettings.optionalRules?.flankingBonus ?? "advantage";
  if (bonus === "advantage") {
    return [
      { key: "flags.midi-qol.advantage.attack.mwak", mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM, value: "true" },
      { key: "flags.midi-qol.advantage.attack.msak", mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM, value: "true" }
    ];
  }
  return [];
}
__name(getFlankingStatusChanges, "getFlankingStatusChanges");
function getFlankedStatusChanges() {
  const bonus = configSettings.optionalRules?.flankingBonus ?? "advantage";
  if (bonus === "advantage") {
    return [
      { key: "flags.midi-qol.grants.advantage.attack.mwak", mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM, value: "true" },
      { key: "flags.midi-qol.grants.advantage.attack.msak", mode: CONST.ACTIVE_EFFECT_MODES.CUSTOM, value: "true" }
    ];
  }
  return [];
}
__name(getFlankedStatusChanges, "getFlankedStatusChanges");
function updateFlankingStatusEffects() {
  const flanking = CONFIG.statusEffects.find((e) => e.id === "flanking");
  const flanked = CONFIG.statusEffects.find((e) => e.id === "flanked");
  if (flanking) flanking.changes = getFlankingStatusChanges();
  if (flanked) flanked.changes = getFlankedStatusChanges();
  if (flanking) {
    ActiveEffect.implementation.fromStatusEffect("flanking").then((effect) => {
      midiFlankingEffect = effect;
      globalThis.MidiQOL.midiFlankingEffect = effect;
    });
  }
  if (flanked) {
    ActiveEffect.implementation.fromStatusEffect("flanked").then((effect) => {
      midiFlankedEffect = effect;
      globalThis.MidiQOL.midiFlankedEffect = effect;
    });
  }
}
__name(updateFlankingStatusEffects, "updateFlankingStatusEffects");
export {
  BaseChatLogClass,
  GameSystemConfig,
  MESSAGE_TYPES,
  MODULE_ID,
  MQActivityMacroLabel,
  MQDamageRollTypes,
  MQItemMacroLabel,
  MQOnUseOptions,
  allAttackTypes,
  busyWait,
  ceInterface,
  createEffects2 as createEffects,
  createMidiMacros,
  debug,
  debugEnabled,
  error2 as error,
  gameStats,
  getCanvas,
  getHealingLabel,
  getStaticID,
  i18n,
  i18nFormat2 as i18nFormat,
  levelsAPI,
  log,
  midiBonusActionEffect,
  midiFlags,
  midiFlankedEffect,
  midiFlankingEffect,
  midiReactionEffect,
  moveToken,
  moveTokenAwayFromPoint,
  overTimeEffectsToDelete,
  removeEffects2 as removeEffects,
  savedOverTimeEffectsToDelete,
  setDebugLevel,
  setupMidiStatusEffects,
  systemConcentrationId,
  teleportToken,
  testfunc,
  timelog,
  updateEffects2 as updateEffects,
  updateFlankingStatusEffects,
  warn
};
//# sourceMappingURL=midi-qol.js.map
