modules/LairActionManagement.js

import { MODULE } from '../module.js';
import { logger } from '../logger.js';
import { ActionDialog } from '../apps/action-dialog.js'
import { queueUpdate } from './update-queue.js'

const NAME = "LairActionManagement";

/** @todo need to support an array of actors, not just a single one */
class LairActionDialog extends ActionDialog {

  /** @override */
  constructor(combatants) {

    /* Localize title */
    const title = MODULE.format("DND5E.LairActionLabel");

    /* construct an action dialog using only legendary actions */
    super(combatants, {lair: true, title, id: 'lairact-action-dialog'});
  }

}


/**
 * LegendaryActionManagement
 *  This Module strictly manages Legendary action economy per the dnd5e rules.
 */
export class LairActionManagement{

  static register(){
    this.settings();
    this.hooks();
  }

  static settings(){
    const config = false;
    const settingsData = {
      lairActionHelper : {
        scope : "world", config, group: "npc-features", default: 0, type: Boolean,
      }
    };

    MODULE.applySettings(settingsData);
  }

  static hooks() {
    Hooks.on('createCombatant', LairActionManagement._createCombatant);

    Hooks.on('updateCombat', LairActionManagement._updateCombat);
  }

  /**
   * Check Combatant for Lair Actions, store information on the combat.
   *  actorid, [itemid], 
   * 
   * @param {Combatant} combatant 
   */
  static _createCombatant(combatant) {

    /* do not run if not the first GM or the feature is not enabled */
    if (!MODULE.isFirstGM() || !MODULE.setting('lairActionHelper')) return;

    const usesLair = getProperty(combatant, "actor.data.data.resources.lair.value")
    const hasLairAction = !!combatant.actor?.items.find((i) => i.data?.data?.activation?.type === "lair")

    /* flag this combatant as a lair actor for quick filtering */
    if (usesLair && hasLairAction){
      queueUpdate( async () => await combatant.setFlag(MODULE.data.name, 'hasLair', true) )
    }

  }

  /**
   * 
   * @param {*} combat 
   * @param {*} changed 
   */
  static _updateCombat(combat, changed) {

    /* do not run if not the first GM or the feature is not enabled */
    if (!MODULE.isFirstGM() || !MODULE.setting('lairActionHelper')) return;

    /* only trigger lair actions on a legit turn change */
    if (!MODULE.isTurnChange(combat, changed)) return;

    const allLairCombatants = combat.combatants.filter( combatant => combatant.getFlag(MODULE.data.name, 'hasLair') );

    
    const previousId = combat.previous?.combatantId;
    const currentId = combat.current?.combatantId;
    
    let previousInit = combat.combatants.get(previousId).initiative;
    const currentInit = combat.combatants.get(currentId).initiative;

    /* check if we have wrapped around and simulate its previous initiative */

    /* lair init should be inside this range or outside? */
    const inside = previousInit - currentInit > 0; 

    const containsLair = (combatant) => {
      const init = combatant.actor.data.data.resources.lair.initiative

      return previousInit >= init && init > currentInit;
    }

    const excludesLair = (combatant) => {
      const init = combatant.actor.data.data.resources.lair.initiative

      return init > currentInit || init <= previousInit;
    }

    const hasHp = (combatant) => {
      return getProperty(combatant.actor, 'data.data.attributes.hp.value') ?? 0 > 0;
    }

    const filterCondition = inside ? containsLair : excludesLair;

    //const triggeredLairInits = allLairCombatants.filter( combatant => correctDirection(combatant) && lairCloser(combatant) && hasHp(combatant) );
    const triggeredLairInits = allLairCombatants.filter( combatant => filterCondition(combatant) && hasHp(combatant) );

    /* send list of combantants to the action dialog subclass */
    if (triggeredLairInits.length > 0) {
      LairActionManagement.showLairActions(triggeredLairInits);
    }

  }

  /** @private */
  /*
   * Generates the action dialog for legendary actions 
   * @param {Array of Object} combatants
   */
  static showLairActions(combatants) {
    new LairActionDialog(combatants).render(true);
  }
}