Larkinabout / fvtt-token-action-hud-core

Token Action HUD is a repositionable HUD of actions for a selected token.
13 stars 18 forks source link

Apply color highlight for buttons when holding shift/ctrl/cmd for Advantage/Disadvantage [FEATURE] #171

Open orcnog opened 1 year ago

orcnog commented 1 year ago

Description Provide color highlighting feedback on buttons if the user is holding a modifier key (ex: shift, ctrl/cmd, or alt) to apply advantage / disadvantage. I've provided a macro that I wrote which highlights text color when the right modifier key is held. Note that it takes into account whether or not Ready Set Roll overrides those native keybinds. There are likely other modules that do something similar that you may want to account for as well -- your call.

(I wish all modules would do this!)

Versions

Additional context For reference: this is my macro, which brings up a list of saving throw options, and also turns all text red on press-n-hold of ctrl/cmd, and green on hold of shift.

saving-throw-color-highlighting

// Pops up a dialog listing all saving throw options. On click, the dialog closes and the saving throw is rolled for one or more selected tokens.
// Holding alt/shift/ctrl/cmd rolls highlights the links with red or green text (indicating adv / disadv) based on the keybindings assigned. It takes into account Ready Set Roll 5e, or uses native FVTT keybinds if that module isn't loaded.

if (!canvas.tokens.controlled.length) {
  ui.notifications.warn('Please select one or more tokens first.');
  return;
}

const savingThrows = {
  str: "Strength",
  dex: "Dexterity",
  con: "Constitution",
  int: "Intelligence",
  wis: "Wisdom",
  cha: "Charisma",
};

(async () => {
  let dialog;

  const readySetRollOwnsKeybind = game.modules.get("ready-set-roll-5e")?.active && game.settings.get("ready-set-roll-5e", "enableAbilityQuickRoll")
  const disAdvModifierKey = (() => {
    if (readySetRollOwnsKeybind) {
      // if Ready Set Roll 5e is installed, use their assigned keybind for disadvantage
      const mode = game.settings.get("ready-set-roll-5e", "rollModifierMode");
      return mode === 0 ? "ctrlKey" : "shiftKey";
    } else {
      // native fvtt keybinding: ctrl/cmd=disadvantage
      return "ctrlKey";
    }
  })();
  const advModifierKey = (() => {
    if (readySetRollOwnsKeybind) {
      // if Ready Set Roll 5e is installed, use their assigned keybind for advantage
      const mode = game.settings.get("ready-set-roll-5e", "rollModifierMode");
      return mode === 1 ? "ctrlKey" : "shiftKey";
    } else {
      // native fvtt keybinding: alt=advantage
      return "altKey";
    }
  })();
  // Click handler for links
  const handleClick = (event) => {
    event.preventDefault();
    event.stopPropagation();

    const savingThrow = event.target.getAttribute("data-saving-throw");

    const selectedTokens = canvas.tokens.controlled;
    selectedTokens.forEach((token) => {
      const rollOptions = { event };
      token.actor.rollAbilitySave(savingThrow, rollOptions);
    });

    dialog.close();
  };

  // Create a dialog with saving throw options
  dialog = new Dialog({
    title: "Roll a Saving Throw",
    content: `
     <div>
       ${Object.entries(savingThrows)
        .map(([key, label]) => `<a class="macro-saving-throw-link" data-saving-throw="${key}">${label}</a>`)
        .join("<br>")}
     </div>
   `,
    buttons: {},
    width: '20rem',
    render: handleRender,
    close: () => {
      const savingThrowLinks = document.getElementsByClassName("macro-saving-throw-link");
      Array.from(savingThrowLinks).forEach((link) => link.removeEventListener("click", handleClick));
    },
  }, {
    width: 120,
    top: event.clientY - 200,
    left: event.clientX - 40
  });

  // Render the dialog
  dialog.render(true);

  function handleRender() {
    // Add event listeners to the saving throw links
    const savingThrowLinks = document.getElementsByClassName("macro-saving-throw-link");
    Array.from(savingThrowLinks).forEach((link) => {
      link.addEventListener("click", handleClick);
      link.style.cursor = "pointer";
    });

    // Apply visual cues for holding shift or alt or ctrl/cmd
    document.addEventListener("keydown", (event) => {
      if (event[advModifierKey]) {
        Array.from(savingThrowLinks).forEach((link) => {
          link.style.color = "green";
        });
      } else if (event[disAdvModifierKey]) {
        Array.from(savingThrowLinks).forEach((link) => {
          link.style.color = "red";
        });
      }
    });

    document.addEventListener("keyup", () => {
      Array.from(savingThrowLinks).forEach((link) => {
        link.style.color = "";
      });
    });
  }
})();
Larkinabout commented 1 year ago

I'm inclined to suggest this might be better as a module in its own right. It looks like you've got a good starter for the code. Why not create a module yourself and add interaction with Foundry VTT core as well as modules of your choosing?

orcnog commented 1 year ago

Hmmm. Yeah it’s something to consider. But, from module to module, there’s no reliable way to query CTAs and know that they trigger adv/disadv-related functionality. Or at least I’m assuming there isn’t. A mod like that sounds hard to maintain… but hey maybe.