├── 5e-polymorph.js
├── 5e-roll-skill.js
├── 8.x-documents.js
├── LICENSE
├── README.md
├── audiohelper.js
├── chat-messages.js
├── compendiums.js
├── dialog.js
├── dice-rolls.js
├── distance.js
├── embeddedEntity-ownedItem.js
├── fetch-json.js
├── ffmpeg
└── animated-gif-to-webm
├── files-and-downloads.js
├── flags.js
├── form-application.js
├── get-entities.js
├── handlebars-templates.js
├── image-popout.js
├── imagemagick
├── bulk-convert-to-webp.ps1
├── bulk-flatten-with-background.ps1
├── single-convert-to-webp
└── single-flatten-with-background
├── macro-in-macro.js
├── macros
├── ddb-importer-monster-splitter
├── debug.js
├── heal-nearby.js
├── lights.js
├── list-modules.js
├── luck.js
├── replace-actors-in-journals.js
├── reset-fog-player.js
├── retrieve-lost-windows.js
├── reveal-fog-of-war.js
├── tile-quick-toggle.js
└── toggle-active-character.js
├── mouse-position.js
├── notification-warning-error.js
├── position.js
├── powershell
└── replace_special_chars.ps1
├── ray-collision.js
├── roll-tables.js
├── settings.js
├── sockets.js
├── status-effects.js
├── tokens.js
├── update.js
└── walls.js
/5e-polymorph.js:
--------------------------------------------------------------------------------
1 | // Polymorph
2 | let form = game.actors.getName('Giant Spider');
3 | canvas.tokens.controlled[0].actor.transformInto(form);
4 |
5 | // Wildshape
6 | let form = game.actors.getName('Giant Spider');
7 | canvas.tokens.controlled[0].actor.transformInto(form, {
8 | keepMental: true,
9 | mergeSaves: true,
10 | mergeSkills: true,
11 | });
12 |
13 | // Transform back (will not work on unliked tokens)
14 | canvas.tokens.controlled[0].actor.revertOriginalForm()
15 |
--------------------------------------------------------------------------------
/5e-roll-skill.js:
--------------------------------------------------------------------------------
1 | // Make a skill (Athletics) check
2 | actor.rollSkill('ath');
3 |
4 | // Skip advantage dialog and roll immediately
5 | actor.rollSkill('ath', { fastForward: true });
6 |
7 | /*
8 | * Available params for 5e's rollSkill():
9 | *
10 | * @param {Array} parts The dice roll component parts, excluding the initial d20
11 | * @param {Object} data Actor or item data against which to parse the roll
12 | * @param {Event|object} event The triggering event which initiated the roll
13 | * @param {string} rollMode A specific roll mode to apply as the default for the resulting roll
14 | * @param {string|null} template The HTML template used to render the roll dialog
15 | * @param {string|null} title The dice roll UI window title
16 | * @param {Object} speaker The ChatMessage speaker to pass when creating the chat
17 | * @param {string|null} flavor Flavor text to use in the posted chat message
18 | * @param {Boolean} fastForward Allow fast-forward advantage selection
19 | * @param {Function} onClose Callback for actions to take when the dialog form is closed
20 | * @param {Object} dialogOptions Modal dialog options
21 | * @param {boolean} advantage Apply advantage to the roll (unless otherwise specified)
22 | * @param {boolean} disadvantage Apply disadvantage to the roll (unless otherwise specified)
23 | * @param {number} critical The value of d20 result which represents a critical success
24 | * @param {number} fumble The value of d20 result which represents a critical failure
25 | * @param {number} targetValue Assign a target value against which the result of this roll should be compared
26 | * @param {boolean} elvenAccuracy Allow Elven Accuracy to modify this roll?
27 | * @param {boolean} halflingLucky Allow Halfling Luck to modify this roll?
28 | * @param {boolean} reliableTalent Allow Reliable Talent to modify this roll?
29 | * @param {boolean} chatMessage Automatically create a Chat Message for the result of this roll
30 | * @param {object} messageData Additional data which is applied to the created Chat Message, if any
31 | */
--------------------------------------------------------------------------------
/8.x-documents.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VanceCole/macros/673a4c1b068cfa8fc9e8273d4b006c07746c840c/8.x-documents.js
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Vance Cole
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Macros & Code Examples
2 | A collection of foundry code examples and simple macros to learn from
3 |
4 | If there is something missing that you think would be helpful, please @vance#1935 on discord!
5 |
6 | ## To be added:
7 | - Array methods (.find, .filter, .map, etc)
8 |
--------------------------------------------------------------------------------
/audiohelper.js:
--------------------------------------------------------------------------------
1 | let src = encodeURI("music/sfx/example.mp3");
2 | AudioHelper.play({
3 | src,
4 | volume: 1,
5 | autoplay: true,
6 | loop: false
7 | }, true);
--------------------------------------------------------------------------------
/chat-messages.js:
--------------------------------------------------------------------------------
1 | // Send chat message
2 | ChatMessage.create({ content: 'Hello World!' });
3 |
4 | // Send chat message under an alias
5 | ChatMessage.create({ content: "Blah blah blah", speaker: { alias: "Steve" } });
6 |
7 | // Send chat message emote as a given actor
8 | let actr = game.actors.getName('Ancient Red Dragon');
9 | let spkr = ChatMessage.getSpeaker({ actr });
10 | ChatMessage.create({
11 | speaker: spkr,
12 | content: "...turns his head toward Steve",
13 | type: CHAT_MESSAGE_TYPES.EMOTE
14 | },
15 | { chatBubble: true });
16 |
17 | // Whisper to player by id
18 | ChatMessage.create({
19 | content: `Hello`,
20 | whisper: ["2UAYUrmMnLCEBiJm"]
21 | });
22 |
23 | // Whisper players by name
24 | ChatMessage.create({
25 | content: `Hello`,
26 | whisper: ChatMessage.getWhisperRecipients('Steve', 'Stella'),
27 | });
28 |
29 | // Whisper GM(s)
30 | ChatMessage.create({
31 | content: `Hello`,
32 | whisper: ChatMessage.getWhisperRecipients('GM'),
33 | });
34 |
--------------------------------------------------------------------------------
/compendiums.js:
--------------------------------------------------------------------------------
1 | // Get data of item in compendium
2 | let pack = game.packs.get('dnd5e.classes');
3 | let content = await pack.getContent();
4 | let data = content.find(i => i.name === 'Barbarian');
--------------------------------------------------------------------------------
/dialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Very simple dialog to display info
3 | */
4 | let d = new Dialog({
5 | title: 'Example',
6 | content: `Hello World!`,
7 | buttons: {
8 | ok: {
9 | icon: '',
10 | label: 'Ok',
11 | },
12 | }
13 | }).render(true);
14 |
15 | /* ---------------------------------------------------- */
16 |
17 | /**
18 | * Very simple dialog to prompt confirmation
19 | * after user clicks button, confirmation will be boolean for yes/no
20 | */
21 | let confirmation = await Dialog.confirm({
22 | title: 'Example Confirm',
23 | content: `
Are you sure?
`,
24 | });
25 |
26 | /* ---------------------------------------------------- */
27 |
28 | /**
29 | * Very simple dialog to request a user input
30 | * Requires 0.7x
31 | */
32 | let myValue = await Dialog.prompt({
33 | content: ``,
34 | callback: (html) => html.find('input').val()
35 | })
36 |
37 | /* ---------------------------------------------------- */
38 |
39 | /**
40 | * Example dialog that requests user input, then uses the value
41 | */
42 | let d = new Dialog({
43 | title: 'Example',
44 | content: `
45 |
67 | `,
68 | buttons: {
69 | no: {
70 | icon: '',
71 | label: 'Cancel'
72 | },
73 | yes: {
74 | icon: '',
75 | label: 'Yes',
76 | callback: (html) => {
77 | let input = html.find('[name="exampleInput"]').val();
78 | let select = html.find('[name="exampleSelect"]').val();
79 | let color = html.find('[name="exampleColor"]').val();
80 | let text = html.find('[name="exampleText"]').val();
81 | console.log(input, select, color, text);
82 | }
83 | },
84 | },
85 | default: 'yes',
86 | close: () => {
87 | console.log('Example Dialog Closed');
88 | }
89 | }).render(true)
90 |
91 | /* ---------------------------------------------------- */
92 |
93 | /**
94 | * Dialog that re-renders (stays open) when button is clicked
95 | * rather than closing
96 | */
97 | let myContent = function (val) {
98 | return `
99 |
100 |
101 | Attack information:
102 |
103 |
104 |
${val}
105 |
106 |
107 | `
108 | }
109 |
110 | let myDialog = new Dialog({
111 | title: `Example Dialog`,
112 | content: myContent('Default Value'),
113 | buttons: {
114 | update: {
115 | label: "Update",
116 | callback: () => {
117 | myValue = `Example random value: ${Math.random()*100}`;
118 | myDialog.data.content = myContent(myValue);
119 | myDialog.render(true);
120 | }
121 | }
122 | },
123 | },
124 | {
125 | id: 'test'
126 | }
127 | ).render(true);
--------------------------------------------------------------------------------
/dice-rolls.js:
--------------------------------------------------------------------------------
1 | // Get result of a roll to use as a variable
2 | let r = new Roll('1d20').roll();
3 |
4 | // Roll to chat message
5 | new Roll('1d20').toMessage();
6 |
7 | /*
8 | * Note that toMessage is async
9 | * This means that if you need to use the result of the roll, you should either
10 | * 1) do so before calling toMessage()
11 | * 2) use await
12 | */
13 | let r = new Roll('1d20').roll();
14 | r.toMessage();
15 | // or
16 | let r = await new Roll('1d20').toMessage();
17 |
18 | // Roll with flavor text
19 | new Roll('1d20').toMessage({ flavor: 'Example Flavor' });
20 |
21 | // Roll with preset roll modes
22 | new Roll('1d20').toMessage({ rollMode: 'roll' }); // Public Roll
23 | new Roll('1d20').toMessage({ rollMode: 'gmroll' }); // Private GM Roll
24 | new Roll('1d20').toMessage({ rollMode: 'blindroll' }); // Blind GM Roll
25 | new Roll('1d20').toMessage({ rollMode: 'selfroll' }); // Self Roll
26 |
--------------------------------------------------------------------------------
/distance.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get euclidean distance between two placeable objects
3 | */
4 | let a = canvas.tokens.placeables[0];
5 | let b = canvas.tokens.placeables[1];
6 | let dist = canvas.grid.measureDistance(a, b);
7 |
8 | /**
9 | * Gets distance between two placeable objects
10 | * for example, tokens, using 5e grid rules
11 | * @param {Placeable} one
12 | * @param {Placeable} two
13 | */
14 | function get5eGridDist(one, two) {
15 | let gs = canvas.grid.size;
16 | let d1 = Math.abs((one.x - two.x) / gs);
17 | let d2 = Math.abs((one.y - two.y) / gs);
18 | let dist = Math.max(d1, d2);
19 | dist = dist * canvas.scene.data.gridDistance;
20 | return dist;
21 | }
22 | let a = canvas.tokens.placeables[0];
23 | let b = canvas.tokens.placeables[1];
24 | let dist = get5eGridDist(a, b);
25 |
26 |
27 | /**
28 | * Gets distance between two placeable objects
29 | * for example, tokens, using 5e ALTERNATE grid rules (5-10-5 movement rule)
30 | * @param {Placeable} one
31 | * @param {Placeable} two
32 | */
33 | function get5eGridDistAlt(one, two) {
34 | let gs = canvas.grid.size;
35 | let d1 = Math.abs((one.x - two.x) / gs);
36 | let d2 = Math.abs((one.y - two.y) / gs);
37 | let maxDim = Math.max(d1, d2);
38 | let minDim = Math.min(d1, d2);
39 | let dist = (maxDim + Math.floor(minDim / 2)) * canvas.scene.data.gridDistance;
40 | return dist;
41 | }
42 | let a = canvas.tokens.placeables[0];
43 | let b = canvas.tokens.placeables[1];
44 | let dist = get5eGridDistAlt(a, b);
45 |
--------------------------------------------------------------------------------
/embeddedEntity-ownedItem.js:
--------------------------------------------------------------------------------
1 | /**
2 | * For dealing with entities embedded in another entity, for example
3 | * item owned by a character you use updateEmbeddedEntity()
4 | */
5 |
6 | // Toggle equip / unequip of actor Steve's Warhammer
7 | let myActor = game.actors.getName('Steve');
8 | let myItem = actor.data.items.find(i => i.name === 'Warhammer');
9 | myActor.updateEmbeddedEntity('OwnedItem', {
10 | _id: myItem._id,
11 | 'data.equipped': !myItem.data.equipped
12 | });
13 |
14 | // You can also use the shortcut method updateOwnedItem, f.x.:
15 | myActor.updateOwnedItem({
16 | _id: myItem._id,
17 | 'data.equipped': !myItem.data.equipped
18 | });
19 |
20 | // Example to change quantity of `Crossbow Bolts` owned by Steve by -1
21 | let myActor = game.actors.getName('Steve');
22 | let myItem = actor.data.items.find(i => i.name === 'Crossbow Bolts');
23 | myActor.updateOwnedItem({
24 | _id: myItem._id,
25 | 'data.quantity': myItem.data.quantity-1
26 | });
27 |
28 | /**
29 | * You can update multiple embeddedEntity in one db operation if required
30 | * Not only can you do this, you very much should especially if operation
31 | * may effect many items
32 | */
33 |
34 | // Example to unequip all items of type "weapon"
35 | let myActor = game.actors.getName('Steve');
36 | let myItems = actor.data.items.filter(i => i.type === 'weapon');
37 | let updates = myItems.map((i) => {
38 | return {
39 | _id: i._id,
40 | 'data.equipped': false
41 | };
42 | });
43 | myActor.updateEmbeddedEntity('OwnedItem', updates);
44 | // myActor.updateOwnedItem(updates); // As before, you can use shortcut updateOwnedItem() instead
45 |
--------------------------------------------------------------------------------
/fetch-json.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Load a json file via fetch
3 | */
4 | fetch('/modules/myModule/module.json')
5 | .then(response => response.json())
6 | .then(data => {
7 | // Do something with the file
8 | console.log(data)
9 | });
10 |
11 | /*
12 | * Load a json file via fetch using await
13 | * (Await is only valid inside an async function)
14 | */
15 | let response = await fetch('/modules/myModule/module.json');
16 | let data = await response.json();
17 | console.log(data);
18 |
--------------------------------------------------------------------------------
/ffmpeg/animated-gif-to-webm:
--------------------------------------------------------------------------------
1 | ffmpeg -i myfile.gif -f webm -c:v libvpx -b:v 1M -auto-alt-ref 0 mynewfile.webm
--------------------------------------------------------------------------------
/files-and-downloads.js:
--------------------------------------------------------------------------------
1 | // Download file to client
2 | saveDataToFile(JSON.stringify({ test: 'hello' }), "application/json", "test.json");
3 |
4 | // Read file from client
5 | const input = $('')
6 | input.on("change", async () => {
7 | const file = input[0].files[0];
8 | if (!file) return;
9 | let contents = await readTextFromFile(file)
10 | console.log(contents);
11 | });
12 | input.trigger('click');
--------------------------------------------------------------------------------
/flags.js:
--------------------------------------------------------------------------------
1 | // Create module flag on a scene
2 | await canvas.scene.setFlag('myModule', 'myFlag', 'myValue');
3 |
4 | // Retrieve module flag on a scene
5 | let x = canvas.scene.getFlag('myModule', 'myFlag');
6 |
7 | // Unset a module flag on a scene
8 | await canvas.scene.unsetFlag('myModule', 'myFlag');
9 |
10 | // Special syntax to unset a nested property
11 | await canvas.scene.setFlag('myModule', 'myFlag.some.nested.-=property', null);
12 |
13 | // React to changes to flag
14 | Hooks.on('updateScene', (scene, data) => {
15 | if (hasProperty(data, 'flags.myModule')) {
16 | console.log(data);
17 | }
18 | });
19 |
20 | // Flags can be arrays, objects, numbers etc as well
21 | // Anything that can be stringified
22 | let data = {
23 | myString: 'hello',
24 | myNum: 42,
25 | myBool: true,
26 | myArray: ['a','b','c']
27 | }
28 | await canvas.scene.setFlag('myModule', 'myFlag', data);
29 |
--------------------------------------------------------------------------------
/form-application.js:
--------------------------------------------------------------------------------
1 | class MyFormApplication extends FormApplication {
2 | constructor(exampleOption) {
3 | super();
4 | this.exampleOption = exampleOption;
5 | }
6 |
7 | static get defaultOptions() {
8 | return mergeObject(super.defaultOptions, {
9 | classes: ['form'],
10 | popOut: true,
11 | template: `myFormApplication.html`,
12 | id: 'my-form-application',
13 | title: 'My FormApplication',
14 | });
15 | }
16 |
17 | getData() {
18 | // Return data to the template
19 | return {
20 | msg: this.exampleOption,
21 | color: 'red',
22 | };
23 | }
24 |
25 | activateListeners(html) {
26 | super.activateListeners(html);
27 | }
28 |
29 | async _updateObject(event, formData) {
30 | console.log(formData.exampleInput);
31 | }
32 | }
33 |
34 | window.MyFormApplication = MyFormApplication;
35 |
36 | /**
37 | * To open your application
38 | */
39 |
40 | new MyFormApplication('example').render(true);
41 |
42 | /**
43 | * myFormApplication.html
44 | */
45 |
56 |
--------------------------------------------------------------------------------
/get-entities.js:
--------------------------------------------------------------------------------
1 | // Get actor / item / scene / journal etc by ID
2 | let actor = game.actors.get("0HcZSUIUZ48WAPyv")
3 | let item = game.items.get("0HcZSUIUZ48WAPyv")
4 | let journal = game.journal.get("0HcZSUIUZ48WAPyv")
5 | let scene = game.scene.get("0HcZSUIUZ48WAPyv")
6 |
7 | // Get actor / item / scene / journal etc by name
8 | let actor = game.actors.getName("Steve")
9 | let item = game.items.getName("Steve's Item")
10 | let journal = game.journal.getName("Steve's Journal")
11 | let scene = game.scene.getName("Steve's House")
12 |
13 | // Get all actors which are Player Characters
14 | let pcs = game.actors.filter(actor => actor.isPC)
15 |
16 | // Get all actors which are npcs
17 | let npcs = game.actors.filter(actor => !actor.isPC)
--------------------------------------------------------------------------------
/handlebars-templates.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Example using handlebars templates in a module
3 | */
4 |
5 | // Preload and/or register handlebars templates and/or partials
6 | Hooks.once('init', () => {
7 | loadTemplates([
8 | 'modules/mymodule/templates/my-template.html',
9 | 'modules/mymodule/templates/my-partial.html'
10 | ]);
11 | });
12 |
13 | // Pass values to handlebars template and get rendered result
14 | let myGreeting = 'Hello';
15 | let myUser = 'Steve';
16 | const myHtml = await renderTemplate(`/path/to/my-handlebars.html`, { myGreeting, myUser });
17 |
18 |
19 | // my-template.html
20 |