Hello,

I want to commission a PF2 system macro that allows to automate "procedural checks" for dungeon crawl and similar games where players stop at regular intervals (doors, rooms) and do the same checks, with predefined characters rolling predefined basic actions (i.e. Seek, Pick a Lock, Force Open).

An example default for a "Door" macro

An example default for "Rummage a Room" macro

The macro will be run by the DM, forcing each character to make the defined rolls. The results could be triggers of the standard posts in chat, though in a perfect would they would fit in their own pretty UI (for that I would pay extra) that makes it very compact for the DM

The macro's functionality

Untitled

Quality of Life functionality

The Macro (made by Magus)

// Dialog's Title
const title = 'Door Checks';

// List of actors you want to list in the Dropdown Menu
const party = ['5YCNP4CvPp6hw7WN', '8A5qtayUjkhqfoYi','Cy3njyMAB5K3eUxm','T9tSdOoarQBOsTz5'];

// Actions you want to take
const actions = [{ name: 'Seek', default: 'T9tSdOoarQBOsTz5' },{ name: 'Seek', default: '5YCNP4CvPp6hw7WN' },{ name: 'Pick a Lock', default: '8A5qtayUjkhqfoYi' },{ name: 'Force Open', default: 'Cy3njyMAB5K3eUxm' }];

const actors = party.map((a) => game.actors.get(a));

if (window.actionDialog?.rendered) {
	return window.actionDialog.close();
}
/*
 * Default Icon to be given in case there is no icon parameter in an Action
 */
const defaultIcon = 'systems/pf2e/icons/actions/craft/unknown-item.webp';

/**
 * This is the list of actions, wich can de edited
 *
 * Action Interface:
 * @param   {string}            name    Name shown as the button text;
 * @param   {string}            skill   Name of the skill, to get the bonus in parenthesis from;
 * @param   {function|string}   action  Function or Macro ID (in case of a string) to call;
 *                                      The call passes an {actors: [actor]} object as argument;
 * @param   {string|undefined}  icon    The icon to be shown in the button. If undefined, will
 *                                      return the defaultIcon;
 */
const actionList = [
	{
		name: 'Aid',
		skill: '',
		action: 'y1rDU9gTz6GVobwf',
		icon: 'systems/pf2e/icons/spells/efficient-apport.webp',
	},
	{
		name: 'Avoid Notice',
		skill: 'Stealth',
		action: game.pf2e.actions.avoidNotice,
		icon: 'systems/pf2e/icons/features/classes/surprice-attack.webp',
	},
	{
		name: 'Balance',
		skill: 'Acrobatics',
		action: game.pf2e.actions.balance,
		icon: '<https://i.imgur.com/q9zXci0.png>',
	},
	{
		name: 'Climb',
		skill: 'Athletics',
		action: game.pf2e.actions.climb,
		icon: 'icons/sundries/misc/ladder.webp',
	},
	{
		name: 'Coerce',
		skill: 'Intimidation',
		action: game.pf2e.actions.coerce,
		icon: '<https://i.imgur.com/iIDYVQL.png>',
	},
	{
		name: 'Command an Animal',
		skill: 'Nature',
		action: game.pf2e.actions.commandAnAnimal,
		icon: '<https://i.imgur.com/5EqnTIS.png>',
	},
	{
		name: 'Craft',
		skill: 'Crafting',
		action: game.pf2e.actions.craft,
		icon: '<https://i.imgur.com/McuTUGb.png>',
	},
	{
		name: 'Create a Diversion - Gesture',
		skill: 'Deception',
		action: "game.pf2e.actions.createADiversion({ event: event, variant: 'gesture' });",
		icon: '<https://i.imgur.com/Rc9vnMX.png>',
	},
	{
		name: 'Create a Diversion - Trick',
		skill: 'Deception',
		action: "game.pf2e.actions.createADiversion({ event: event, variant: 'trick' });",
		icon: '<https://i.imgur.com/u9Pt7q1.png>',
	},
	{
		name: 'Demoralize',
		skill: 'Intimidation',
		action: game.pf2e.actions.demoralize,
		icon: 'icons/skills/social/intimidation-impressing.webp',
	},
	{
		name: 'Disarm',
		skill: 'Athletics',
		action: game.pf2e.actions.disarm,
		icon: 'icons/skills/melee/sword-damaged-broken-glow-red.webp',
	},
	{
		name: 'Feint',
		skill: 'Deception',
		action: game.pf2e.actions.feint,
		icon: '<https://i.imgur.com/EIRuIFp.png>',
	},
	{
		name: 'Follow an Expert',
		skill: '',
		action: 'eHtbauiO3n075GUu',
		icon: 'systems/pf2e/icons/spells/favorable-review.webp',
	},
	{
		name: 'Force Open',
		skill: 'Athletics',
		action: game.pf2e.actions.forceOpen,
		icon: '<https://i.imgur.com/BkwG0cZ.png>',
	},
	{
		name: 'Gather Information',
		skill: 'Diplomacy',
		action: game.pf2e.actions.gatherInformation,
		icon: 'icons/skills/social/diplomacy-handshake.webp',
	},
	{
		name: 'Grapple',
		skill: 'Athletics',
		action: game.pf2e.actions.grapple,
		icon: 'icons/skills/melee/unarmed-punch-fist.webp',
	},
	{
		name: 'Hide',
		skill: 'Stealth',
		action: game.pf2e.actions.hide,
		icon: 'icons/magic/nature/stealth-hide-eyes-green.webp',
	},
	{
		name: 'Impersonate',
		skill: 'Deception',
		action: game.pf2e.actions.impersonate,
	},
	{
		name: 'Jump - High',
		skill: 'Athletics',
		action: game.pf2e.actions.highJump,
		icon: '<https://i.imgur.com/Ot4sbDt.png>',
	},
	{
		name: 'Jump - Long',
		skill: 'Athletics',
		action: game.pf2e.actions.longJump,
		icon: '<https://i.imgur.com/Ot4sbDt.png>',
	},
	{
		name: 'Lie',
		skill: 'Deception',
		action: game.pf2e.actions.lie,
		icon: 'icons/magic/control/mouth-smile-deception-purple.webp',
	},
	{
		name: 'Make an Impression',
		skill: 'Diplomacy',
		action: game.pf2e.actions.makeAnImpression,
		icon: 'icons/environment/people/commoner.webp',
	},
	{
		name: 'Maneuver in Flight',
		skill: 'Acrobatics',
		action: game.pf2e.actions.maneuverInFlight,
		icon: 'icons/commodities/biological/wing-bird-white.webp',
	},
	{
		name: 'Pick a Lock',
		skill: 'Thievery',
		action: game.pf2e.actions.pickALock,
		icon: '<https://i.imgur.com/cb7ejb2.png>',
	},
	{
		name: 'Raise a Shield',
		skill: '',
		action: game.pf2e.actions.raiseAShield,
		icon: 'systems/pf2e/icons/actions/raise-a-shield.webp',
	},
	{
		name: 'Recall Knowledge',
		skill: '',
		action: 'EBdLzeLZyUju18Af',
		icon: 'systems/pf2e/icons/features/classes/imperial.webp',
	},
	{
		name: 'Repair',
		skill: 'Crafting',
		action: game.pf2e.actions.repair,
		icon: '<https://i.imgur.com/McuTUGb.png>',
	},
	{
		name: 'Request',
		skill: 'Diplomacy',
		action: game.pf2e.actions.request,
		icon: 'icons/skills/social/thumbsup-approval-like.webp',
	},
	{
		name: 'Seek',
		skill: 'Perception',
		action: game.pf2e.actions.seek,
		icon: '<https://i.imgur.com/7mTBOrS.png>',
	},
	{
		name: 'Sense Direction',
		skill: 'Survival',
		action: game.pf2e.actions.senseDirection,
		icon: 'icons/tools/navigation/compass-brass-blue-red.webp',
	},
	{
		name: 'Sense Motive',
		skill: 'Perception',
		action: game.pf2e.actions.senseMotive,
		icon: '<https://i.imgur.com/7mTBOrS.png>',
	},
	{
		name: 'Shove',
		skill: 'Athletics',
		action: game.pf2e.actions.shove,
		icon: '<https://i.imgur.com/Expmoz8.png>',
	},
	{
		name: 'Sneak',
		skill: 'Stealth',
		action: game.pf2e.actions.sneak,
		icon: 'icons/magic/nature/stealth-hide-eyes-green.webp',
	},
	{
		name: 'Squeeze',
		skill: 'Acrobatics',
		action: game.pf2e.actions.squeeze,
		icon: '<https://i.imgur.com/sQ6eUA4.png>',
	},
	{
		name: 'Swim',
		skill: 'Athletics',
		action: game.pf2e.actions.swim,
		icon: 'icons/creatures/fish/fish-shark-swimming.webp',
	},
	{
		name: 'Take Cover',
		skill: '',
		action: 'PC0y5A6tHrtJhqVL',
		icon: '<https://i.imgur.com/5TgWEZF.png>',
	},
	{
		name: 'Track',
		skill: 'Survival',
		action: game.pf2e.actions.track,
		icon: '<https://i.imgur.com/RgHVtlC.png>',
	},
	{
		name: 'Treat Disease',
		skill: 'Medicine',
		action: game.pf2e.actions.treatDisease,
		icon: 'icons/magic/nature/root-vine-caduceus-healing.webp',
	},
	{
		name: 'Treat Poison',
		skill: 'Medicine',
		action: game.pf2e.actions.treatPoison,
		icon: 'systems/pf2e/icons/effects/treat-poison.webp',
	},
	{
		name: 'Treat Wounds',
		skill: 'Medicine',
		action: 'BO2iFo14pFYHEIGa',
		icon: 'icons/magic/nature/root-vine-caduceus-healing.webp',
	},
	{
		name: 'Trip',
		skill: 'Athletics',
		action: game.pf2e.actions.trip,
		icon: '<https://i.imgur.com/Sr2Gu19.png>',
	},
	{
		name: 'Tumble Through',
		skill: 'Acrobatics',
		action: game.pf2e.actions.tumbleThrough,
		icon: '<https://i.imgur.com/33UF9Ok.png>',
	},
].sort((a, b) => -(a.name < b.name));

const getSkills = (actor) => {
	return { perception: actor.attributes.perception, ...actor.skills };
};

const createMenu = (actionName) => {
	const action = actionList.find((a) => a.name === actionName.name);
	const idx = actionList.indexOf(action);

	const options = actors.map((actor) => `<option value="${actor.id}" ${actor.id === actionName.default ? 'selected' : ''}>${actor.name}</option>`).join('');

	return `
    <div class="form-group">
        <label class="centered"><img src="${action.icon ?? defaultIcon}" height="24"/> ${action.name}</label>
        <select name="${action.name}">
                    ${options}
        </select>
        <button class="small" data-action="${idx}">
            <i class="fas fa-dice-d6"></i>
            Roll
        </button>
    </div>
    `;
};

const content = `
<style>
label.centered {
    display: flex;
    justify-content: flex-start;
}
label.centered img {
    margin-right: 10px;
}
button.small {
    flex: 1;
    margin-left: 5px;
}
</style>
<form autocomplete="off" spellcheck="off">
    ${actions.map((action) => createMenu(action)).join('')}
</form>
`;

window.actionDialog = new Dialog(
	{
		title,
		content,
		buttons: {
			rollAll: {
				icon: `<i class="fas fa-dice"></i>`,
				label: 'Roll All',
				callback: (html) => {
					const buttons = html.querySelectorAll('form button');
					if (game.user.getFlag('pf2e', 'settings.showRollDialogs')) {
						for (let i = buttons.length; i > 0; i--) buttons[i - 1].click();
						return;
					}
					buttons.forEach((btn) => btn.click());
				},
			},
		},
		default: 'rollAll',
		render: (html) => {
			actionDialog.submit = function (button, event) {
				try {
					if (button.callback) button.callback(this.options.jQuery ? this.element : this.element[0], event);
				} catch (err) {
					ui.notifications.error(err);
					throw new Error(err);
				}
			};
			const action = (button) => {
				const idx = button.dataset.action;
				const action = actionList[idx];
				const actor = actors[party.indexOf(button.parentElement.querySelector('select').value)];

				if (typeof action.action === 'string') game.macros.get(action.action)?.execute();
				else action.action({ actors: [actor] });
			};
			html.querySelectorAll('form button').forEach((button) => button.addEventListener('click', () => action(button)));
		},
	},
	{ jQuery: false }
).render(true);

PF2 Action Functions

Action Name Skill PF2 System Function that can be called as a macro
Avoid Notice Stealth game.pf2e.actions.avoidNotice({ event: event });
Balance Acrobatics game.pf2e.actions.balance({ event: event });
Climb Athletics game.pf2e.actions.climb({ event: event });
Coerce Intimidation game.pf2e.actions.coerce({ event: event });
Command an Animal Nature game.pf2e.actions.commandAnAnimal({ event: event });
Craft Crafting game.pf2e.actions.craft({ event: event });
Create a Diversion - Gesture Deception game.pf2e.actions.createADiversion({ event: event, variant: 'gesture' });
Create a Diversion - Trick Deception game.pf2e.actions.createADiversion({ event: event, variant: 'trick' });
Demoralize Intimidation game.pf2e.actions.demoralize({ event: event });
Disarm Athletics game.pf2e.actions.disarm({ event: event });
Feint Deception game.pf2e.actions.feint({ event: event });
Force Open Athletics game.pf2e.actions.forceOpen({ event: event });
Gather Information Diplomacy game.pf2e.actions.gatherInformation({ event: event });
Grapple Athletics game.pf2e.actions.grapple({ event: event });
Hide Stealth game.pf2e.actions.hide({ event: event });
Impersonate Deception game.pf2e.actions.impersonate({ event: event });
Jump - High Athletics game.pf2e.actions.highJump({ event: event });
Jump - Long Athletics game.pf2e.actions.longJump({ event: event });
Lie Deception game.pf2e.actions.lie({ event: event });
Make an Impression Diplomacy game.pf2e.actions.makeAnImpression({ event: event });
Maneuver in Flight Acrobatics game.pf2e.actions.maneuverInFlight({ event: event });
Pick a Lock Thievery game.pf2e.actions.pickALock({ event: event });
Raise a Shield Macro game.pf2e.actions.raiseAShield({ actors: [token?.actor ?? actor ?? game.user.character].filter((actor) => actor) })
Recall Knowledge Macro see Recall Knowledge macro
Repair Crafting game.pf2e.actions.repair({ event: event });
Request Diplomacy game.pf2e.actions.request({ event: event });
Seek Perception game.pf2e.actions.seek({ event: event });
Sense Direction Survival game.pf2e.actions.senseDirection({ event: event });
Sense Motive Perception game.pf2e.actions.senseMotive({ event: event });
Shove Athletics game.pf2e.actions.shove({ event: event });
Sneak Stealth game.pf2e.actions.sneak({ event: event });
Squeeze Acrobatics game.pf2e.actions.squeeze({ event: event });
Swim Athletics game.pf2e.actions.swim({ event: event });
Take Cover Macro see Take Cover macro
Track Survival game.pf2e.actions.track({ event: event });
Treat Disease Medicine game.pf2e.actions.treatDisease({ event: event });
Treat Poison Medicine game.pf2e.actions.treatPoison({ event: event });
Treat Wounds Macro see Treat Wounds macro
Trip Athletics game.pf2e.actions.trip({ event: event });
Tumble Through Acrobatics game.pf2e.actions.tumbleThrough({ event: event });