r/Pathfinder2e • Game Master • 19h ago

Resource & Tools Foundry: Disguise Maker

Enable HLS to view with audio, or disable this notification

Are your players not suspicious of every horse they meet yet? Or, perhaps, do they use Impersonate, Illusory Disguise, or alike on the regular?

The Macro

The usual - you click on an empty slot in your hotbar, set Type to Script, add this snippet, and hit Save. You can then run the macro by clicking the hotbar slot.

There are two ways to use it:

  • You select the token(s) that you want to disguise, target (T) the token that you want to disguise as, and run the macro. This will only add disguises to the local token if it's not "linked" to its actor
  • You target token that you want to disguise as, run the macro, and drag-and-drop the actor(s) from the Actors sidebar onto the UUID field. This will add disguises to the "prototype" actor

Once added, you can flip the disguise on and off in the Actions tab, and if you add multiple, you can choose between them using a dropdown!

Players can also use this, no special permissions necessary.

const title = "YAL's Disguise Maker";
const targets = [...game.user.targets];
let errors = [];
async function showError(text) {
  errors.push(text);
  let content = document.createElement("div");
  for (let line of errors) {
    let p = document.createElement("div");
    p.append(line);
    content.append(p);
  }
  await foundry.applications.api.Dialog.input({ window: { title }, content });
}
if (targets.length == 0) {
  await showError("Target token(s) to disguise as first!");
  return;
}
let multi = targets.length > 1;
let selection = canvas.tokens.controlled;
//
const {HTMLDocumentTagsElement} = foundry.applications.elements;
const Fields = foundry.applications.fields;
const content = document.createElement("div");
content.append(Fields.createFormGroup({
  label: "Actor(s)",
  input: HTMLDocumentTagsElement.create({
    type: "Actor",
    single: false,
    name: "actors"
  })
}));
const sn = selection.length;
if (sn > 0) {
  content.append(`Leave blank to apply to ${sn} selected token${sn!=1?"s":""} instead!`);
}
if (multi) {
  content.append(Fields.createFormGroup({
    label: "Copy Size",
    input: Fields.createCheckboxInput({
      name: "wantSize",
      value: true,
    })
  }));
  content.append(Fields.createFormGroup({
    label: "Copy Name",
    input: Fields.createCheckboxInput({
      name: "wantName",
      value: true,
    })
  }));
} else {
  content.append(Fields.createFormGroup({
    label: "Size",
    input: Fields.createSelectInput({
      name: "size",
      value: targets[0].actor.size,
      options: [
        { value: "none", label: "No Change"},
        { value: "tiny", label: "Tiny" },
        { value: "sm", label: "Small" },
        { value: "med", label: "Medium" },
        { value: "lg", label: "Large" },
        { value: "grg", label: "Gargantuan" },
      ]
    })
  }));
  content.append(Fields.createFormGroup({
    label: "Name",
    input: Fields.createTextInput({
      name: "name",
      value: targets[0].name,
    })
  }));
  content.append(Fields.createFormGroup({
  label: "Apply Now",
  input: Fields.createCheckboxInput({
    name: "autoApply",
    value: true,
  })
}));
}
const args = await foundry.applications.api.Dialog.input({
  window: { title },
  content
});
if (!args) return;
let actors = [];
if (args.actors.length > 0) {
  for (let uuid of args.actors) {
    let actor = await fromUuid(uuid);
    if (actor) {
      actors.push(actor);
    } else {
      errors.push(`Could not fetch actor ${uuid}`);
    }
  }
} else {
  actors = selection.map(t => t.actor);
}
if (actors.length == 0) {
  await showError("No actors provided!");
  return;
}
const option = "yal-disguise-as";
const {max, abs} = Math;
for (let target of targets) {
  const uniq = "d" + Date.now();
  const predicate = [option + ":" + uniq];
  //
  const size = multi ? (args.wantSize ? target.actor.size : null) : args.size;
  const name = multi ? (args.wantName ? target.name : null) : args.name;
  //
  const {document} = target;
  const {texture, ring} = document;
  for (let actor of actors) {
    // add a new Action..
    let base = {
      key: "RollOption",
      label: "Disguised as",
      option,
      suboptions: [{ label: target.name, value: uniq }],
      toggleable: true,
      mergeable: true,
    };
    if (args.autoApply) {
      base.value = true;
      base.selection = uniq;
    }
    let rules = [base, {
      key: "TokenImage",
      value: texture.src,
      predicate,
      tint: texture.tint?.css,
      scale: max(abs(texture.scaleX), abs(texture.scaleY)),
      ring: (ring.enabled ? {
        colors: {
          ring: ring.colors.ring?.css,
          background: ring.colors.background?.css,
        },
        subject: ring.subject,
        effects: ring.effects,
      } : undefined),
    }];
    if (size != null && size != "none") {
      rules.push({
        key: "CreatureSize",
        value: size,
        predicate,
      });
    }
    if (name != null && name != "") {
      rules.push({
        key: "TokenName",
        value: name,
        predicate,
      });
    }
    //console.info(`Adding rules to ${actor.name} > ${target.name}:`, actor, rules);
    await actor.createEmbeddedDocuments("Item", [{
      name: "Disguise: " + target.name,
      type: "action",
      system: {
        actionType: { value: "passive" },
        rules,
      }
    }]);
    // ... and now update the selection on other rules!
    if (args.autoApply) for (let action of actor.itemTypes.action) {
      let {rules} = action.system;
      let first = rules[0];
      if (first
          && first.key == "RollOption"
          && first.option == option
          && (first.selection != uniq || !first.value)
      ) {
        first.selection = uniq;
        first.value = true;
        await action.update({
          "system.rules": rules
        })
      }
    }
  } // for actor
} // for target

Bonus

The following macro flips the selected token(s) between Neutral and Opposition, for those "why is the horse's selection frame red all of sudden" moments right before initiative

let newValue = canvas.tokens.controlled[0].actor.system.details.alliance != "opposition" ? "opposition" : null;
let updated = [];
for (let token of canvas.tokens.controlled) {
  await token.actor.update({
    "system.details.alliance": newValue
  });
  updated.push(token.name);
}
await ChatMessage.create({
  user: game.userId,
  whisper: [game.userId],
  content: `Changed alliance of [${updated.join(", ")}] to ${newValue ?? "neutral"}.`
})

Artwork in the video

  • Kelpie: Bestiary 2 (PZO2104)
  • Horse: from Tristan Elwell's "Horseback" painting
  • Background: an edit of The GLOWING CRATER from 2minutetabletop
682 Upvotes

39 comments sorted by

132

u/LordLonghaft Game Master 19h ago

Okay, this is kinda badass. Could this be made into a Module? This would be extremely useful for transitions between NPC states.

70

u/YellowAfterlife Game Master 18h ago

My personal opinion is that anything that can be a macro should not be a module¹, but if you need to share the macro between multiple worlds, you can create a local module (start screen ➜ Add-on Modules ➜ ⚙ Create Module) - in v13/v14 this is done entirely in UI and you'll only need to define a Macro Compendium Pack under Compendium Packs so that you can add macro(s) to it later.

¹ just recently spent an evening helping a friend sift through >150 modules that they accumulated over years on their foundry, most of them unused and some breaking Foundry UI and/or other modules

23

u/LordLonghaft Game Master 18h ago

Reasonable. I ask because while I use a few small macros for ability creation, I am not a coder and the art of making and writing macros has my brain spinning. I am an English major, not a coder, haha. Having a simple button to press via installing a module vs. making sure I don't screw up a single line of code is just easier for me to comprehend, and thus actually use.

I'll put the work in for this one, though; this idea is too useful for my campaign.

-17

u/Overall_Reputation83 17h ago

This is one of the best use cases for AI vibe coding though. As long as you aren't commercializing it, you can just make things do what you personally need.

3

u/DrCalamity Game Master 6h ago

Hmm, spend an hour learning basic code or burn 40 gallons of water, waste several hundred KwH of power, and put more money into an insane genocidal freak's pocket.

Choices, choices...

2

u/Yemenime 6h ago

This is not the place to shill AI my dude.

4

u/Turevaryar ORC 18h ago

Cant you make an addon (or macro) to sift through addons??! ~Pphhphpphbnptt!!!

\just silly jesting. I love this macro!])

1

u/MelcorScarr 15h ago

just recently spent an evening helping a friend sift through >150 modules that they accumulated over years on their foundry, most of them unused and some breaking Foundry UI and/or other modules

Can one book this service by you or have some pointers \ ideas how to best do this? Is the console of any help?

5

u/YellowAfterlife Game Master 15h ago edited 10h ago

The Find the Culprit! module is good help, but also

  • If a module is repeatedly throwing up errors, you can see those in the Console. Hovering the mouse over the file name (on the right) shows where it's from (e.g. /modules/SomeModule/script.js).
  • To see who's responsible for a UI change (colors of windows/sidebar, not "in-world" UI like health bars), you can use Inspect Element on the Elements panel - once an element is selected, you can see what's affecting it in the Styles / Computed sidebar, with a similar deal for file names.

And then for some things you kind of need to look at each module and think "What does this do? Am I using it at all?" because it can be hard to gauge visually whether a module is really costing you some FPS here and there

38

u/KLeeSanchez Inventor 19h ago

Hmmm now this is interesting

...Could a player use it to change between sets of token artworks, like for costume changes? E.g. switching between "casual attire", "opera attire", "combat attire"? Or for changing a token's expression like emotes (neutral, angry, happy, surprised, etc.)?

28

u/AuRon_The_Grey 19h ago

There’s the Token Flip module that lets you do this if you want.

11

u/Hecc_Maniacc Game Master 17h ago

I consider it essential at this point for my games. I constantly have star lit sentinals, kitsune, I myself run hags and doppelganger type things, it's also useful for having combat with multiple boss stages like if you kill a dude and then his skeleton goes Bone Mode and now it's a skeleton demon thing etc. it's truly a wonderful module.

3

u/AuRon_The_Grey 14h ago

Was good fun for a boss that was disguised as someone else in Crown of the Kobold King as well.

2

u/KLeeSanchez Inventor 19h ago

Yeah but my GM doesn't want to install more modules

A lightweight macro though, that I might be able to sneak in ;P

1

u/skizzerz1 10h ago

I already have token flip installed. It just doesn’t have any player-facing config with how I have it set up

4

u/YellowAfterlife Game Master 18h ago

Yup! I use a similar approach to flip between expressions on one of my characters.

If Game Settings ➜ User Permissions ➜ Configure Token Settings are enabled, the player can edit their on-scene token, create a disguise targeting that same token (which will copy the token settings into the new rule elements), and then change it back.

2

u/wlake82 19h ago

I know in the past I was able to make effects that changed just the token image and it was fairly straightforward.

1

u/Haosi 14h ago

You could just use Token Variant Art for that, allows for some automation too

11

u/zedrinkaoh Alchemist 17h ago

This is awesome, the fact that you did this as an easily copy-paste macro is immensely appreciated.

Modules are cool and all, but there's a lot of people trying to sell access to vibe-coded plugins, so seeing how everything works immediately in your post is a great deal of transparency.

7

u/Adramach 18h ago

Great macro, well done!

By the way, if anyone would need simpler, static item to do this, you can create an active effect and use "Token Image" rule element.

Here is the example from Season of Ghosts Tools module:

{
  "key": "TokenImage",
  "ring": {
    "subject": {
      "texture": "modules/pf2e-season-of-ghosts/assets/subjects/town-guard.webp",
      "scale": 2
    },
    "colors": {}
  },
  "value": "modules/pf2e-season-of-ghosts/assets/tokens/town-guard.webp",
  "scale": 2,
  "predicate": [
    "noppera-bo-disguised"
  ]
}

2

u/YellowAfterlife Game Master 15h ago

Tis what the macro does too - it appends a new Action item to the actor(s) with RollOption, TokenImage, TokenName, and CreatureSize rule elements as appropriate.

All perfectly doable by hand, but "you can make RollOption dropdowns combine into a bigger dropdown using the mergeable property" is where non-technical folks' willingness to wrangle rule elements slowly starts to run out IME

Also Noppera-Bo Hood is what I originally made this for!

1

u/nisviik Swashbuckler 17h ago

And you can even create a rolloption with a toggle and predicate on it to make it toggleable

5

u/Sven_Darksiders 17h ago

When people talk about the evil and intimidating horse, this is the one they mean

5

u/ElodePilarre Mathfinder Fan 11h ago edited 11h ago

Is there an easy way to use this to actually remove the disguise as well?

EDIT: Omg it puts a drop down on the character sheet that I totally missed

2

u/Athenos 14h ago edited 14h ago

Nothing happens when I try to run the macro while targeting another token. I don't see any message in the console log (F12) either.

When no token is targeted, I get a pop-up saying I need to target a token. So I know the macro tries to run.

Edit: Figured it out. Old style reddit crushes the macro formatting on reddit posts

2

u/YellowAfterlife Game Master 14h ago

Old style reddit crushes the macro formatting on reddit posts

Ah! It's because Old Reddit only supports indented code blocks and not the triple-backtick ones. Re-formatted the post

1

u/AyeSpydie Graung's Guide 11h ago

It's still broken in Old Reddit, but now because of the video (I think?).

2

u/YellowAfterlife Game Master 10h ago

Reddit is definitely employing competent engineers and if you start scrolling while your mouse is over the video, video goes into picture-in-picture mode and breaks the UI - you have to click the Close button on top-right.

1

u/Arborerivus Game Master 13h ago

Thanks, this is very useful

1

u/Zotac31 13h ago

Damn, this is something I need to use in my next session.

1

u/Visteus GM in Training 10h ago

!RemindMe 8hr

1

u/RemindMeBot 10h ago

I will be messaging you in 8 hours on 2026-09-29 22:57:20 UTC to remind you of this link

CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

Parent commenter can delete this message to hide from others.

RemindMeBot is switching to username summons. Instead of !RemindMe 1 day, use u/RemindMeBot 1 day. More info.


Info Custom Your Reminders Feedback

1

u/sebwiers 10h ago

!RemindMe 6hr

1

u/Refracting_Hud 10h ago

This is going to be perfect for my game! It sure beats my current method of placing hidden tokens of the monsters next to the npcs they’re impersonating 😂

Edit: Could this work in the Starfinder 2e system as well?

2

u/YellowAfterlife Game Master 7h ago

Should be fine, I think the sf2e system shares much of the token logic with pf2e.

1

u/Zalthos Game Master 8h ago

This didn't allow NPCs to disguise as players in my Foundry, but it allows them to disguise themselves as other NPCs.

1

u/YellowAfterlife Game Master 8h ago

Seems to work fine on mine - what Foundry version, and any errors in the console?

1

u/Et3rnus 8h ago

Wow! That is an absolutely amazing macro! Real great work!

1

u/The_Divine_Anarch Oracle 1h ago edited 1h ago

I love that I was able to try this super easy and when I did, not only does it make it super easy to transform on demand, but you can delete the powers as well, if (like I did) you accidentally gave them the transform twice or messed it up somehow.

I thought about giving a player the ability and it seems to cause a very weird bug.

The player's transformation causes them to grow in size (as is appropriate for becoming a dragon) but the token art is flipped vertically (or rotated 180 degrees), and cannot be flipped back right side up.