From 1c61d6dba9b8d0e6f7696ed6fc7b016f476dd2f4 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Fri, 15 May 2026 09:03:49 -0700 Subject: [PATCH 01/24] Add files via upload --- PinNote/1.0.1/PinNote.js | 354 +++++++++++++++++++++++++++++++++++++++ PinNote/PinNote.js | 3 +- PinNote/script.json | 4 +- 3 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 PinNote/1.0.1/PinNote.js diff --git a/PinNote/1.0.1/PinNote.js b/PinNote/1.0.1/PinNote.js new file mode 100644 index 0000000000..5e35e7665c --- /dev/null +++ b/PinNote/1.0.1/PinNote.js @@ -0,0 +1,354 @@ +// Script: PinNote +// By: Keith Curtis +// Contact: https://app.roll20.net/users/162065/keithcurtis + +const pinnote = (() => { +//(() => { + 'use strict'; + + const version = '1.0.1'; //version number set here + log('-=> PinNote v' + version + ' is loaded.'); + //Changelog + //1.0.1 gmtext fix + //1.0.0 Debut + + + const SCRIPT_NAME = 'PinNote'; + + const isGMPlayer = (playerid) => playerIsGM(playerid); + + const getTemplate = (name) => { + if (typeof Supernotes_Templates === 'undefined') { + return null; + } + if (!name) return Supernotes_Templates.generic; + const key = name.toLowerCase(); + return Supernotes_Templates[key] || Supernotes_Templates.generic; + }; + + const sendGenericError = (msg, text) => { + if (typeof Supernotes_Templates === 'undefined') return; + + const t = Supernotes_Templates.generic; + sendChat( + SCRIPT_NAME, + t.boxcode + + t.titlecode + SCRIPT_NAME + + t.textcode + text + + '' + + t.footer + + '' + ); + }; + + +const normalizeHTML = (html) => { + if (!html) return html; + + return html + .replace(/\r\n/g, '') // Windows line endings + .replace(/\n/g, '') // Unix line endings + .replace(/\r/g, ''); // Old Mac line endings +}; + + + /* ============================================================ + * HEADER COLOR ENFORCEMENT + * ============================================================ */ + + const enforceHeaderColor = (html, template) => { + if (!html) return html; + + const colorMatch = template.textcode.match(/color\s*:\s*([^;"]+)/i); + if (!colorMatch) return html; + + const colorValue = colorMatch[1].trim(); + + return html.replace( + /<(h[1-4])\b([^>]*)>/gi, + (match, tag, attrs) => { + + if (/style\s*=/i.test(attrs)) { + return `<${tag}${attrs.replace( + /style\s*=\s*["']([^"']*)["']/i, + (m, styleContent) => + `style="${styleContent}; color: ${colorValue};"` + )}>`; + } + + return `<${tag}${attrs} style="color: ${colorValue};">`; + } + ); + }; + + /* ============================================================ */ + + const parseArgs = (content) => { + const args = {}; + content.replace(/--([^|]+)\|([^\s]+)/gi, (_, k, v) => { + args[k.toLowerCase()] = v.toLowerCase(); + return ''; + }); + return args; + }; + + const extractHandoutSection = ({ handout, subLink, subLinkType }) => { + return new Promise((resolve) => { + + if (!handout) return resolve(null); + + if (!subLink) { + const field = subLinkType === 'headerGM' ? 'gmnotes' : 'notes'; + handout.get(field, (content) => resolve(content || null)); + return; + } + + if (!['headerplayer', 'headergm'].includes(subLinkType?.toLowerCase())) { + return resolve(null); + } + + const field = subLinkType.toLowerCase() === 'headergm' + ? 'gmnotes' + : 'notes'; + + handout.get(field, (content) => { + if (!content) return resolve(null); + + const headerRegex = /<(h[1-4])\b[^>]*>([\s\S]*?)<\/\1>/gi; + let match; + + while ((match = headerRegex.exec(content)) !== null) { + const tagName = match[1]; + const innerHTML = match[2]; + const stripped = innerHTML.replace(/<[^>]+>/g, ''); + + if (stripped === subLink) { + const level = parseInt(tagName[1], 10); + const startIndex = match.index; + + const remainder = content.slice(headerRegex.lastIndex); + + const stopRegex = new RegExp( + `]*>`, + 'i' + ); + + const stopMatch = stopRegex.exec(remainder); + + const endIndex = stopMatch + ? headerRegex.lastIndex + stopMatch.index + : content.length; + + return resolve(content.slice(startIndex, endIndex)); + } + } + + resolve(null); + }); + }); + }; + + const transformBlockquoteMode = (html) => { + + const blockRegex = /]*>([\s\S]*?)<\/blockquote>/gi; + + let match; + let lastIndex = 0; + let playerContent = ''; + let gmContent = ''; + let found = false; + + while ((match = blockRegex.exec(html)) !== null) { + found = true; + gmContent += html.slice(lastIndex, match.index); + playerContent += match[1]; + lastIndex = blockRegex.lastIndex; + } + + gmContent += html.slice(lastIndex); + + if (!found) { + return { player: '', gm: html }; + } + + return { player: playerContent, gm: gmContent }; + }; + + on('chat:message', async (msg) => { + if (msg.type !== 'api' || !msg.content.startsWith('!pinnote')) return; + + if (typeof Supernotes_Templates === 'undefined') { + sendChat(SCRIPT_NAME, `/w gm PinNote requires Supernotes_Templates to be loaded.`); + return; + } + + const args = parseArgs(msg.content); + const isGM = isGMPlayer(msg.playerid); + + if (!msg.selected || msg.selected.length === 0) + return sendGenericError(msg, 'No pin selected.'); + + const sel = msg.selected.find(s => s._type === 'pin'); + if (!sel) + return sendGenericError(msg, 'Selected object is not a pin.'); + + const pin = getObj('pin', sel._id); + if (!pin) + return sendGenericError(msg, 'Selected pin could not be resolved.'); + + const isSynced = + !pin.get('notesDesynced') && + !pin.get('gmNotesDesynced') && + !pin.get('imageDesynced'); + + const linkType = pin.get('linkType'); + + /* ============================================================ + * LINKED HANDOUT MODE + * ============================================================ */ + + if (isSynced && linkType === 'handout') { + + const handoutId = pin.get('link'); + const subLink = pin.get('subLink'); + const subLinkType = pin.get('subLinkType'); + const autoNotesType = pin.get('autoNotesType'); + + const handout = getObj('handout', handoutId); + if (!handout) + return sendGenericError(msg, 'Linked handout not found.'); + + let extracted = await extractHandoutSection({ + handout, + subLink, + subLinkType + }); + + if (!extracted) + return sendGenericError(msg, 'Requested section not found in handout.'); + + const template = getTemplate(args.template); + if (!template) return; + + const sender = pin.get('title') || SCRIPT_NAME; + const titleText = subLink || sender; + + if (subLink) { + const headerStripRegex = /^]*>[\s\S]*?<\/h[1-4]>/i; + extracted = extracted.replace(headerStripRegex, ''); + } + + let to = (args.to || 'pc').toLowerCase(); + if (!isGM) to = 'pc'; + + let whisperPrefix = ''; + const extractingGM = subLinkType?.toLowerCase() === 'headergm'; + + let visibleContent = extracted; + let gmBlock = ''; + + if (autoNotesType === 'blockquote') { + + const transformed = transformBlockquoteMode(extracted); + + visibleContent = enforceHeaderColor(transformed.player, template); + + if (transformed.gm && to !== 'pc') { + gmBlock = + `
` + + enforceHeaderColor(transformed.gm, template) + + `
`; + } + + } else { + visibleContent = enforceHeaderColor(visibleContent, template); + } + + if (extractingGM) { + whisperPrefix = '/w gm '; + } else if (to === 'gm') { + whisperPrefix = '/w gm '; + } else if (to === 'self') { + whisperPrefix = `/w "${msg.who}" `; + } + + const html = + template.boxcode + + template.titlecode + titleText + + template.textcode + + (visibleContent || '') + + gmBlock + + '' + + template.footer + + ''; + + sendChat(sender, whisperPrefix + normalizeHTML(html)); + + return; + } + + /* ============================================================ + * CUSTOM PIN MODE + * ============================================================ */ + + if ( + !pin.get('notesDesynced') && + !pin.get('gmNotesDesynced') && + !pin.get('imageDesynced') + ) { + return sendGenericError( + msg, + 'This pin is not desynced from its linked handout.' + ); + } + + const notes = (pin.get('notes') || '').trim(); + if (!notes) + return sendGenericError(msg, 'This pin has no notes to display.'); + + let to = (args.to || 'pc').toLowerCase(); + if (!isGM) to = 'pc'; + + let whisperPrefix = ''; + if (to === 'gm') whisperPrefix = '/w gm '; + else if (to === 'self') whisperPrefix = `/w "${msg.who}" `; + + const template = getTemplate(args.template); + if (!template) return; + + const sender = pin.get('title') || SCRIPT_NAME; + + let imageBlock = ''; + const tooltipImage = pin.get('tooltipImage'); + if (tooltipImage) { + imageBlock = + ``; + } + + const coloredNotes = enforceHeaderColor(notes, template); + + let gmBlock = ''; + if (isGM && to !== 'pc' && pin.get('gmNotes')) { + gmBlock = + `
` + + enforceHeaderColor(pin.get('gmNotes'), template) + + `
`; + } + + const html = + template.boxcode + + template.titlecode + sender + + template.textcode + + imageBlock + + coloredNotes + + gmBlock + + '' + + template.footer + + ''; + + sendChat(sender, whisperPrefix + normalizeHTML(html)); + + }); + +})(); \ No newline at end of file diff --git a/PinNote/PinNote.js b/PinNote/PinNote.js index 1f12c2e945..5e35e7665c 100644 --- a/PinNote/PinNote.js +++ b/PinNote/PinNote.js @@ -6,9 +6,10 @@ const pinnote = (() => { //(() => { 'use strict'; - const version = '1.0.0'; //version number set here + const version = '1.0.1'; //version number set here log('-=> PinNote v' + version + ' is loaded.'); //Changelog + //1.0.1 gmtext fix //1.0.0 Debut diff --git a/PinNote/script.json b/PinNote/script.json index 04e5cccc59..1864e46913 100644 --- a/PinNote/script.json +++ b/PinNote/script.json @@ -1,7 +1,7 @@ { "name": "PinNote", "script": "PinNote.js", - "version": "1.0.0", + "version": "1.0.1", "description": "# PinNote\n\nPinNote sends information from linked or custom map pins to chat using any Supernotes template. Supernotes must be installed for this script to function.\n\n---\n\n## Arguments\n\nArguments are case-insensitive and use the format:\n\n```\n--key|value\n```\n\n---\n\n### --to|\n\nControls where the message is sent.\n\n#### --to|pc\n\nSends a public message to chat.\n\n- GM notes are never included.\n\n---\n\n#### --to|gm\n\nWhispers the message to the GM only.\n\n- GM notes are included.\n\n---\n\n#### --to|self\n\nWhispers the message to the invoking player.\n\n- GM notes are included only if the invoker is a GM.\n\n---\n\nIf a non-GM runs the command, --to is ignored and treated as pc.\n\n---\n\n### --template| (optional)\n\nSelects a Supernotes display template.\n\n- If omitted or invalid, the generic template is used silently.\n\n---\n\n## Examples\n\n```\n!pinnote\n!pinnote --to|gm\n!pinnote --to|self --template|dark\n!pinnote --template|wizard\n```\n\n---\n\n## Requirements\n\n- Exactly one map pin must be selected.\n - If none are selected, the script reports an error.\n - If multiple are selected, only the first pin is used.\n\n- The pin may be a linked pin or a custom pin.\n - If linked to a handout, the script pulls the relevant section from the handout.\n - If custom, the script uses the Notes field of the pin.\n\n- A custom pin must contain notes.\n - If the Notes field is empty, nothing is sent and an error is shown.\n\n- Supernotes must be installed.\n - If missing, the script exits and notifies the GM.\n\n---\n\nType **!pinnote** in chat to use the script.", "authors": "Keith Curtis", "roll20userid": "162065", @@ -11,5 +11,5 @@ "handout": "read" }, "conflicts": [], - "previousversions": ["1.0.0"] + "previousversions": ["1.0.0","1.0.1"] } \ No newline at end of file From 23d3724adfe12070b2ee9606bb22024ba72ffa0f Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Sat, 16 May 2026 15:43:30 -0700 Subject: [PATCH 02/24] Add Patreon link to script.json --- Align/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Align/script.json b/Align/script.json index 5808186572..cb3d992582 100644 --- a/Align/script.json +++ b/Align/script.json @@ -5,6 +5,7 @@ "description": "Visually align, distribute, snap, stack, and scatter tokens on the VTT with a chat menu interface. Features include grid snapping, spacing control, z-index sorting, random scatter, undo, and edge protection. Now contains token page management", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "graphics": "read,write", From 74f627ea643143329d8387f12c3794b31ec8ca3f Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:47:29 -0700 Subject: [PATCH 03/24] Add Patreon link to script.json --- Wiki/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Wiki/script.json b/Wiki/script.json index ef857e2ab6..e356f28094 100644 --- a/Wiki/script.json +++ b/Wiki/script.json @@ -5,6 +5,7 @@ "description": "A unified interface for browsing Roll20 handouts and map pins with navigation, filtering, and history tracking.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "handout": "read,write" }, From afec91407b5f002bba8a542f6891f3331210122d Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:48:12 -0700 Subject: [PATCH 04/24] Update script.json --- Ping Buddy/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Ping Buddy/script.json b/Ping Buddy/script.json index 607e50a030..370eff3da2 100644 --- a/Ping Buddy/script.json +++ b/Ping Buddy/script.json @@ -5,7 +5,8 @@ "description": "## Ping Buddy\nPing Buddy adds some extra utilitiy to the ping pull function, beyond what you would get with a long press or a shift-long press.\n### Follow a Party Token\nIf you create a token and name it 'Party', or set the tooltip to the word 'Party', the screen will ping pull to the token whenever it is moved. This allows you to guide players around the map or across an overland map. The ping animataion is temporarily disabled. The circle animation is removed by temporarily setting the GM's player color to transparent, and then reverting it. It is possible if you make too many rapid movements in succession, it won't revert the color before running again and stick the GM color on transparent. If this happens, just re-assign the GM player color manually.\nIf you set the 'Party' token to be an invisible token (marking it with an aura only you can see), it allows you to center the player view to any area of the map without a visual cue.\nIf two or more party tokens are on the same page, the screen will ping pull only to the one that was last moved.\nAnother use would be a board or wargame where you want to call attention to the last piece moved. Just make sure 'Party' is in the nameplate or the Tooltip. More behaviors are explained below.\n### Tooltip Codes\nInstead of naming the token 'Party', you can set the token's tooltip to the word 'Party' for the same behavior, and name the token as you will. These will only work if the player is on the page with the player flag. Capitalization does not matter. There are four codes currently:\n- **Party:** Behavior is same as naming the token 'Party'. All players will follow the token without animation.\n- **Ping:** Behavior is same as naming the token 'Party', but all players will follow the token with animation.\n- **Pingme:** Token will pingpull only the controller. They will see animation, but no one else will be affected.\n- **Pingmesilent:** Token will pingpull only the controller. They will not see animation, and no one else will be affected.\n### Pull Players to the Start of a Page\nWhenever the player flag is moved to a page, all players on that page will immediately be ping pulled to the 'Party' token. The token may be moved to the gm layer if you wish, and players will be pulled to that part of the map. This is useful for encounter maps that don't start in the upper left corner.\n### Find a Token\n**Find My Token:** If a player types `!pingme` into chat, the screen will ping pull to the first token it finds that they control. If they control no tokens on the screen, the script will inform you. This is useful for large maps, or if a player loses track of their token in Dynamic Lighting or Fog of War.\n**Find token by name:** If you type a name after !pingme, separated by a space, the script will find the first occurrence on the page of a token with that name. Ex. `!pingme Bob` will ping pull the first token it finds named 'Bob'. Spaces and capitalization count.\nPingme commands will only work if the player is on the page with the player flag.", "authors": "Keith Curtis", "roll20userid": "162065", - "modifies": [], + "patreon": "https://www.patreon.com/c/KeithCurtis", + "modifies": [], "conflicts": [], "previousversions": ["0.0.7"] } From 43a268426e6ace9f69b5140dddbf133f7b0332a2 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:48:28 -0700 Subject: [PATCH 05/24] Add Patreon link to campaign survey script --- Campaign Survey/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Campaign Survey/script.json b/Campaign Survey/script.json index 40501a1549..86140fd7b6 100644 --- a/Campaign Survey/script.json +++ b/Campaign Survey/script.json @@ -5,6 +5,7 @@ "description": "**Campaign Survey** *([Forum thread](https://app.roll20.net/forum/post/10675897/script-campaign-survey))* is a script that tries to give the user a bird-eye view of their campaign, reporting on how much space is taken up by which elements. Some elements have more of an impact on game performance than others, and in general, the script tries to show them in descending order of importance. Information on improving Roll20 performance can be found on [Roll20 system Recommendations](https://help.roll20.net/hc/en-us/articles/4403128607127-Roll20-System-Recommendations) and [Optimizing Roll20 Performance](https://help.roll20.net/hc/en-us/articles/360041544654) in the Help Center. The !survey command can be followed by ' --' and one or more of the following keywords:\r\r**characters** A list of all character sheets in a campaign in descending order of complexity. All entries are linked to the sheet so you can quickly open them and investigate. Each character is appended by the number of attributes it contains. This will vary depending on the sheet used, and complexity of the character. Look for exceptionally high outliers, typically spell caster with many spells.\r\r**attributes** A list of all character attributes in a campaign. This is a very rough indicator of the size of the Firebase DB, when combined with the number of graphics placed on the VTT. A high number will likely affect loading time, and may contribute to lag. Character sheets, and the number of graphics and drawings on the VTT are the greatest contributors to game size.\r\r**abilities** A list of all character sheets in a campaign in descending order of number of abilities (macros resident on character sheets). Abilities can be quite long, but they are still relatively low impact compared to attributes. All entries are linked to the sheet so you can quickly open them and investigate. Each character is appended by the number of abilities it contains. Look for exceptionally high outliers, typically Macro Character Sheets. In this case, a high number of macros is rarely an issue, since this practice serves to consolidate a lot of a games automation in one place. For more information see, [Macro Character Sheet](https://tinyurl.com/3jn6jzrr) in the [Roll20 Community Wiki](https://tinyurl.com/mtz43sun).\r\r**pages** A list of all pages in a campaign, along with the number of graphics on that page. Graphics are listed as total graphics/count of tokens. Look for exceptionally high count outliers. More graphics = more Firebase items to track. Number of graphics is more likely to be a lag concern on Dynamically Lit pages.\r\r**lighting** A list of all dynamically lit pages in a campaign, along with the number of sighted tokens, light-emitting tokens, plus DL paths, and the total number of points in those paths. Simplify when possible. There is overlap in the report, if one token is emitting low and bright light, it is listed in both counts. See [System Requirements & Best Practices](https://help.roll20.net/hc/en-us/articles/360045793374-System-Requirements-Best-Practices) in the Help Center for performance help, or Help Center's [Dynamic Lighting headquarters](https://help.roll20.net/hc/en-us/categories/360003712734-Dynamic-Lighting). If Dynamic Lighting does not seem to be working, try going through the [Dynamic Lighting Checklist](https://help.roll20.net/hc/en-us/articles/360044771413-Dynamic-Lighting-Checklist).\r\r**graphics** A list of all graphics in a campaign, followed by the number of times they are used. You may occasionally see a 1x and a say, 6x for the same graphic. This likely indicates that an item was dragged in from a marketplace source and used again from the art library. Using the alternate arguments of **ugraphics** or **mgraphics** will constrain the report to User Art Library or Marketplace items respectively. Marketplace items can be identified by a thin black border around the graphics frame. Clicking on a graphic lists all occurrences of the identified graphic in a campaign, broken out by page, with the source of the graphic clearly labeled. Information on best practices for creating and uploading graphics can be found in [Best Practices for Files on Roll20](https://help.roll20.net/hc/en-us/articles/360037256634-Best-Practices-for-Files-on-Roll20s) in the Help Center\r\r**handouts** A list of all handouts in a campaign. All entries are linked to the handout so you can quickly open them and investigate. Handouts are very low impact items, and are unlikely to contribute to lag or increased loading time except in extreme numbers.\r\r**macros** A list of all macros in a campaign. Each macro will play the macro when clicked. Macros are low impact items.\r\r**tables A list of all tables in a campaign in alphabetical order. All entries are linked to a roll on that table. Clicking on the name will produce a gmroll, clicking on the number of entries will perform an inline roll. Tables are low impact items.\r\r**texts** A list of all text obejcts in a campaign. Text objects are low impact items. There may be some entries for empty texts. This can happen when the user clicks with the text editing tool in error and then abandons the process to do something else. Empty texts are not a serious problem, but in order to clean things up and make it easier not select them by accident. There is a tool to remove all empty texts. Use with caution.\r\r**decks** A list of all card decks in a campaign, along with a card count for each deck. Decks are a relatively low-impact item, unless there are an inordinate number of cards in play on the VTT.\r\r**tracks** A list of all jukebox tracks in a campaign. Clicking on the track will play the track. Each track name has a button next to it that will stop playing that track. Jukebox tracks are streaming files and will not affect loading time. They are unlikely to contribute to lag, except in cases where there is already low bandwidth.\r\r**players** A list of all players in a campaign, along with their player id, and avatar color. Clicking the player color box calls up a character list report formatted like the characters keyword in descending order of complexity, but is limited to characters for whom the player is listed as a controller. Clicking on the player name will take you to their public Roll20 profile page, in case you need to PM them or can't remember what they are called outside of the game.\r\r**makehandout** Sends the report to a handout named 'Campaign Survey Report'. You can use this with any other keyword or as the sole keyword. Every category on the campaign overview display has a handout option. There is a button at the bottom of the display to directly open the handout. All reports sent to the handout will update in real time. If you want the name of the campaign to appear at the top of every report, put it in the gmnotes of the Campaign Survey Report handout.\r\r**overview** _(optional)_ gives a count of each of the categories. Each category is clickable to send that command to chat or to the report. typiing **!survey --overview**, is the same as typing **!survey**.\r\r**Help** Displays this help information.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "ability.*": "read, write" }, From 12d5bc17b5c28fed42d82dde0c941da5368ba111 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:48:44 -0700 Subject: [PATCH 06/24] Update script.json --- AutoLinker/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AutoLinker/script.json b/AutoLinker/script.json index 25b1f33145..41c36b204f 100644 --- a/AutoLinker/script.json +++ b/AutoLinker/script.json @@ -5,6 +5,7 @@ "description": "# Autolinker\n\n## Purpose\n\nAutolinker converts bracketed shorthand written in the Notes or GMNotes fields of handouts and characters into clickable Roll20 journal or compendium links when the entry is saved. This extends the basic linking functions built into Roll20.\n\n## General Usage\n\nThese formats may be used in the Notes or GMNotes fields of any handout or character.\n\nNote: The script runs after a save event. Because the handout may refresh before processing finishes, you may need to close and reopen the handout (or click Edit again) to see the updated links.\n\n## Journal Links\n\n[goblin|Jimmy]\n\nCreates a link to the handout or character named goblin, displayed as Jimmy.\n\nIf no display text is provided, standard Roll20 journal linking rules apply.\n\n## Compendium Links\n\n[5e:fireball]\n\nLinks to the D&D 5e compendium entry for fireball.\n\n[5e:wall of fire|the wall]\n\nLinks to the D&D 5e compendium entry for wall of fire, displayed as the wall.\n\n### Supported Compendium Prefixes\n\n- 5e: — D&D 5th Edition\n- pf2: — Pathfinder 2nd Edition\n\n## Handout Header Linking\n\nHeader links apply to handouts only and use the # character.\n\n### Link to a Header in Another Handout\n\n[Dungeon of Doom#6. Zombie Chorus|See Room 6]\n\nLinks to the header 6. Zombie Chorus in the handout Dungeon of Doom, displayed as See Room 6.\n\n### Link to a Header in the Same Handout\n\n[#6. Zombie Chorus|See Room 6]\n\nLinks to the header 6. Zombie Chorus in the current handout, displayed as See Room 6.\n\n### Omit Display Text\n\n[#6. Zombie Chorus]\n\nIf no display text is supplied, the header text is used as the link text.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "handouts": "read,write", @@ -14,4 +15,4 @@ "previousversions": [ "1.0.0", "1.0.1" ] -} \ No newline at end of file +} From e98b31a9f5ea9241608700ee08e4eba661f982c1 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:49:19 -0700 Subject: [PATCH 07/24] Add Patreon link and update script description Added Patreon link for support and updated the script description. --- Reporter/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Reporter/script.json b/Reporter/script.json index 0186e8a4c5..ce27c9b8f5 100644 --- a/Reporter/script.json +++ b/Reporter/script.json @@ -5,6 +5,7 @@ "description": "**Reporter** reads the tokens on the board that are associated with character sheets and builds a report of them in the chat or to a handout, returning selected values from the token settings or the character sheets they are associated with.\rReporter Reporter has specific support for the D&D 5th Edition by Roll20, D&D5E Shaped, Pathfinder First Edition by Roll20, and Pathfinder Second Edition by Roll20 sheet. There is an option to choose other for the sheet, which will disable the few sheet-specific shortcuts. It should work with most any sheet or no sheet at all. The first time you run the script, it will ask you to choose which sheet you are using. You can change this behavior with !report --config|sheet\rYou can either select a set of tokens to work with, or if you select no tokens, it will assume all tokens on the Object/Token layer. This behavior can be altered using keywords, described below. The basic syntax is:\r`!report --[queries] ---[buttonline] ----[keywords]`\r****\r### Queries\rQueries are constructed using\r`t|attribute` to poll a token attribute\r`c|attribute` to poll a character sheet attribute\r**Examples**\r`--t|name` would return a report of all selected token names\r`--c|strength` would return a report of all strength values on the character sheets of the selected tokens\rFor character sheets, the script will try to pull a value from the character journal first, and if that does not exist, the installed character sheet.\r### Dividers\rYou may not always want each attribute reported on its own line. You can add a code after the attribute name(not the alias) to use something either than a line return between attributes\r**comma (`,`)** Adds three non-breaking spaces between this attribute and the next, keeping them on the same line when possible.\r**period (`.`)** Adds a vertical pipe between this attribute and the next, keeping them on the same line when possible.\r**dash (`-`)** Adds a thin gray horizontal rule between this attribute and the next.\r**Hashtag (`#`)** Adds a bit of horizontal space between this attribute and the next.\r_Examples:_\r`!report --t|emits_bright_light,|Bright-Light t|bright_light_distance|Distance t|emits_low_light,|Low-Light t|low_light_distance|Distance ---light`\rwill return this Bright Light and Distance on the first line of each record amd Low-Light and its Distance on the second, instead of each value taking up its own line\r\r### Aliases\rThere are times in the report when you would not like `has_bright_light_vision` in the report. You can substitute an alias for the attribute name that will display in chat. For this, just add another pipe after the query and type an alias.\r\rFor example, if a token has 60 feet of Night Vision:\r`t|night_vision_distance`\rmight produce:\rT: night_vision_distance = 60\rbut\r`t|night_vision_distance?NV`\rwould yield:\rT: NV= 60\r\r### Buttonline\rThe buttonline is a string containing text and Ability or API Command buttons. These are formed using the normal syntax for such things with a few exceptions.\rIn order to keep the Roll20 parser from resolving queries and attribute calls before the script gets them, they need to be written slightly differently.\r_Examples:_\r**@{token|name}** is written as **A{token|name}**\r**?{question|default_answer}** is written as Q**{question|default_answer}**\r\rFurther, for a handful of scripts, the Reporter API will attempt to parse the code so that each buttonline refers to the specific token being reported on. Currently **Token Mod**, **ChatSetAttr**, and **Supernotes** are supported.\r\r### Filters\rThere are four types of operator.\r`+` only includes the token/character pairs that matches the query\r`-` excludes any token/character pair that exactly matches the query\r`~` only includes any character that is a partial match for the query\r`^` excludes any character that is a partial match for the query\rthus:\r`!report||-|c|name|Goblin` will return all tokens that are not represented by the Goblin character sheet.\r`!report||~|c|name|Goblin` will return any tokens that are represented by the Goblin or Hobgoblin character sheet.\r`!report||-|c|npc|1||+|t| has_night_vision|true` will exclude all NPCs (leaving only PCs), and then only return those that have nightvision set.\rFilters do not support an alias, because they are never displayed in the final report.\rFilters are executed sequentially, with each filter working on the result from the last, so some logic is required for best results.\rFilters are case insensitive.\rThere is as yet, no way to test for an empty, or undefined value, however, the keyword `hideempty|true` will cause the report to suppress the display of empty values.\r\r### Special Codes\rReporter contains a few special codes for common cases, to make macro writing easier. You can put thes in place of normal commands:\r`--vision` as the Query will replace any declared query line with one designed to report most vision situations. It will give values for whether the token has sight, night vision and what the distance of any night vision is.\r`---vision` as the Buttonline will replace any declared button line with a buttonline designed to handle most cases of vision and darkvision.\r`--light` as the Query will replace any declared query line with one designed to report most lighting situations. It will give values for the amount of light, distance and what type.\r`---light` as the Buttonline will replace any declared button line with a buttonline designed to handle most cases of lighting.\r`---actions` as the Buttonline will replace any declared button line with a buttonline made up of the token action buttons associate with the character. This is designed for synergy with the Token Action Maker script, but is not essential. Not that the token actions created by this command cannot contain roll templates and will not convert the {selected|commandname} structure where it might appear in an ability. This requires very careful parsing and is best avoided. It should work flawlessly with Token Action Maker commands, with the exception of the *Check* and *Save* buttons (which it will skip), for the reasons just mentioned.\r\r### Keywords\rkeywords change the overall appearance or scope of the report. They are separated from the rest of the report by four dashes and must come at the end.\r`layer|[gmlayer|objects|map|walls|tracker|all]` will constrain the report to a particular layer or all layers at once, so long as no tokens are selected. If any tokens are selected, Reporter will default to the layer the selected tokens are on. This makes it easier for instance to check the vision settings of tokens on the token layer and the gmlayer simultaneously, or to ping pull to note tokens on the gm layer without switching manually to that layer. \rIf the layer keyword all is used the report will be on all token/character pairs on all layers. In this case, a layer character will appear on each subhead line of the report to let you know which layer the token is on. \rIf the layer keyword tracker is used the report will be on all token/character pairs on the Turn Tracker as if it were a layer. In this case, a layer character will appear on each subhead line of the report to let you know which layer the token is on. If you click on the layer token, it will switch the token from the GM/Notes layer to the Token/Objects layer and back.\r`compact|[true|false]` _(default=false):_ The compact mode shows the token image at half size, and eliminates the second line of the report subhead, since it is not always desired. You may have a very large report you want to see better, or you may be using a sheet that does not support the default values. Currently the second line of the subhead only references the _D&D 5th Edition by Roll20_ and _Pathfinder Second Edition by Roll20_ Sheets. \r`showheader|[true|false]` _(default=true):_ This will control whether the header will display at the top of the report. \r`showfooter|[true|false]` _(default=true):_ This will control whether the footer will display at the bottom of the report. \r`printbutton|[true|false]` _(default=true):_ This will control whether the print button will display on each line of the report. \r`notesbutton|[true|false]` _(default=false):_ This will control whether a notes button will display on each line of the report. This notes button will return the token notes for the token on that line. The visibility of the notes button is controlled by the visibility keyword. If the visibility is *gm*, it will use a !gmnote command, if the If the visibility is *whisper*, it will use a !selftnote command, and if the visibility is *all*, it will use a !pcnote command. \r`visibility|[gm|whisper|all]` _(default=gm):_ This will determine how the report is presented. *gm* is whispered to the gm, *whisper* is whispered to the user who sent the command, *all* is posted openly for all to see. \r`showfooter|[true|false]` _(default=true):_ This will control whether the footer will display at the bottom of the report. \r`source|[true|false]` _(default=true):_ if source is set to false, the C and T characters that show whether an attribute comes fromthe token or the sheet will not be displayed. Use this is they are a distraction. \r`charactersheetlink|[true|false]` _(default=true):_ if this keyword is set to false, the link to open the token*s corresponding character sheet will not display \r`subtitle|[true|false]` _(default=true):_ if this keyword is set to false, the line directly below the character name will not display. (This is also the default in Compact mode). This may be desirable if not using the _D&D 5th Edition by Roll20_ or _Pathfinder Second Edition by Roll20_ Sheets. \r`ignoreselected|[true|false]` _(default=false):_ if this keyword is set to true, the search will not be preset to whichever tokens are selected. The report will run as if no tokens were selected, following whatever layer criteria might have been specified. \r`npcsubstitutions[true|false]` _(default=true):_ if this keyword is set to false, the script will not automatically substitute npc attributes for their PC counterparts (ex: npc_senses for passive_wisdom).This is good for sheets that are not the _D&D 5th Edition by Roll20_ or _Pathfinder Second Edition by Roll20_ Sheets. \r`sort|attribute` _(default is the raw order):_ This keyword will sort the final list. Most of the sorts are confined to the token attributes, since they require internal code and if they refer to a sheet may return poor or no results if the sheet does not have the proper attributes. Currently the following values can be sorted on: \r- charName: character name. Sheet must have a *name* attribute.\r- charNameI: character name, inverse order. Sheet must have a *name* attribute.\r- tokenName: token name\r- tokenNameI: token name, inverse order.\r- bar1: token bar1 value\r- bar1I: token bar1 value, inverse order.\r- bar2: token bar2 value\r- bar2I: token bar2 value, inverse order.\r- bar3: token bar3 value\r- bar3I: token bar3 value, inverse order.\r- cr - Challenge Rating. D&D 5th Edition by Roll20 Sheet only\r- crI - Challenge Rating, inverse order. D&D 5th Edition by Roll20 Sheet only\r`title|Title|` If this is present in the keywords, the string in between pipes will be placed at the top of the report. If you only want the custom title to display, be sure turn off the header with showheader|false.\r`handout|Handoutname|` If this is present in the keywords, the report will be sent to a handout instead of chat. This can allow a report to remain usable without scrolling through the chat. It can also be used as a sort of floating palette. Reports in handouts can be updated. Running the macro again will regenerate the table, as will pressing the Repeat button. The string in between pipes will be used as the name of the report handout. If no handout by that name exists, Reporter will create one and post a link in chat to open it. The title must be placed between two pipes. handout|My Handout| will work. handout|My Handout will break.\rA report Handout automatically creates a horizontal rule at the top of the handout. Anything typed manually above that rule will be persistent. Reporter will not overwrite it. You can use this area to create Journal Command Buttons to generate new reports or to give some context to the existing report. All updates are live.\r**Supernotes Buttons**\rThese are small buttons that will appear on each line of the report that call up Supernotes commands. These buttons require Supernotes to be installed (Available from the Roll20 One Click installer). If Supernotes is not installed, the buttons will still display but will have no effect. If the report is in the Chat tab, the notes will display in the chat tab, and if the report is set to be in a handout, the notes will in the handout, directly below the report. This can be used to create a handout that can run a report and display notes below. An example use could be a handout that can read map pins and display the notes for each map pin, making an interactive city guide. \r`tokennotesbutton|[true|false]` _(default=false):_ If this keyword is set to true, the report will place a small shortcut button to return the contents of the reported tokens GM Notes field.\r`charnotesbutton|[true|false]` _(default=false):_ If this keyword is set to true, the report will place a small shortcut button to return the contents of the GM Notes field of the character assigned to the reported token.\r`biobutton|[true|false]` _(default=false):_ If this keyword is set to true, the report will place a small shortcut button to return the contents of the Bio Notes field of the character assigned to the reported token.\r`avatarbutton|[true|false]` _(default=false):_ If this keyword is set to true, the report will place a small shortcut button to return the Avatar of the character assigned to the reported token.\r`tooltipbutton|[true|false]` _(default=false):_ If this keyword is set to true, the report will place a small shortcut button to return contents of the reported tokens Tooltips field.\r`imagebutton|[true|false]` _(default=false):_ If this keyword is set to true, the report will place a small shortcut button to return images from the Bio field of the character assigned to the reported token.\rSee this thread in the Roll20 Forums for more details - [Reporter Feedback thread](https://app.roll20.net/forum/post/10381135/script-reporter-1-dot-x)", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "ability.*": "read, write" }, From c374d761e091891250ae13ec3a413d28d7e09e76 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:49:41 -0700 Subject: [PATCH 08/24] Add Patreon link to script.json Added Patreon link for support. --- Format Table/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Format Table/script.json b/Format Table/script.json index 5d995cd52f..464793bf47 100644 --- a/Format Table/script.json +++ b/Format Table/script.json @@ -5,10 +5,11 @@ "description": "Displays a chat menu for auto-formatting handout tables with a variety of presets resembling those used by popular game systems. Supports 5.5e, 5e, DnD 3, Invisible, Minimal, OSR, Pathfinder 2, Roll20 Default, Wikitable", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "handout": "read,write" }, "dependencies": [], "conflicts": [], "previousversions": ["1.0.0"] -} \ No newline at end of file +} From 45187904a082f2d3678756b4cca198a711bea25a Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:50:14 -0700 Subject: [PATCH 09/24] Add Patreon link to script.json --- Dealer/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Dealer/script.json b/Dealer/script.json index 6fc7dd812c..1ba56de928 100644 --- a/Dealer/script.json +++ b/Dealer/script.json @@ -5,6 +5,7 @@ "description": "Deals and takes cards from players by deck\r\rSyntax is:\r`!deal --[give,take] [#] --[deck name]|[Card Name]`\r\rIf give/take is not specified, it gives a card to the player controlling the selected token If deck name is not specified, it defaults to \"Playing Cards\". If a card name is not specified, it defaults to a random card from the specified deck.\r\rYou can specify a number of cards to give or take. After the action word, type an integer, after a space:\r\r`!deal --give 5 --Playing Cards`\r\rYou can specify a card to deal by name. If no card exists by that name in that deck, the script will inform the user by chat message. Note that it is possible to give multiple copies of the same card even from a finite deck:\r\r`!deal --give --Playing Cards|Six of Hearts`\r\rThe script will deal cards to the player from the specified deck so long as there are enough available. If the deck has cycled through all cards, it will automatically shuffle.\r\rIf a token has more than one controller or is controlled by All and one or more players, it will select the first single player in the controlled by list.\r\rScript will try to let you know if you have not prepared a command or deck properly.\r\rIf deck does not deal a card, you may need to manually shuffle (Roll20 bug). If the deck is shuffled, it may not recognize all cards in hand.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "hand.*": "read,write", From 26804c0b495f90020545a7af95ccd979647b1877 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:50:30 -0700 Subject: [PATCH 10/24] Add Patreon link to script.json --- Token Reference/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Token Reference/script.json b/Token Reference/script.json index 34638faca3..d5d49cbb02 100644 --- a/Token Reference/script.json +++ b/Token Reference/script.json @@ -5,7 +5,8 @@ "description": "Displays a styled reference card in chat for tokens on a designated page, including image, character link, and formatted GM notes. ", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "conflicts": [], "previousversions": ["1.0.0"] -} \ No newline at end of file +} From c2d028947e990d5f7aee2e7b557168df1dc0f3ae Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:50:48 -0700 Subject: [PATCH 11/24] Add Patreon link to script.json --- PInNote/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/PInNote/script.json b/PInNote/script.json index 8f07f9ec09..0043c26c06 100644 --- a/PInNote/script.json +++ b/PInNote/script.json @@ -5,6 +5,7 @@ "description": "# PinNote\n\nPinNote sends information from linked or custom map pins to chat using any Supernotes template. Supernotes must be installed for this script to function.\n\n---\n\n## Arguments\n\nArguments are case-insensitive and use the format:\n\n```\n--key|value\n```\n\n---\n\n### --to|\n\nControls where the message is sent.\n\n#### --to|pc\n\nSends a public message to chat.\n\n- GM notes are never included.\n\n---\n\n#### --to|gm\n\nWhispers the message to the GM only.\n\n- GM notes are included.\n\n---\n\n#### --to|self\n\nWhispers the message to the invoking player.\n\n- GM notes are included only if the invoker is a GM.\n\n---\n\nIf a non-GM runs the command, --to is ignored and treated as pc.\n\n---\n\n### --template| (optional)\n\nSelects a Supernotes display template.\n\n- If omitted or invalid, the generic template is used silently.\n\n---\n\n## Examples\n\n```\n!pinnote\n!pinnote --to|gm\n!pinnote --to|self --template|dark\n!pinnote --template|wizard\n```\n\n---\n\n## Requirements\n\n- Exactly one map pin must be selected.\n - If none are selected, the script reports an error.\n - If multiple are selected, only the first pin is used.\n\n- The pin may be a linked pin or a custom pin.\n - If linked to a handout, the script pulls the relevant section from the handout.\n - If custom, the script uses the Notes field of the pin.\n\n- A custom pin must contain notes.\n - If the Notes field is empty, nothing is sent and an error is shown.\n\n- Supernotes must be installed.\n - If missing, the script exits and notifies the GM.\n\n---\n\nType **!pinnote** in chat to use the script.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": ["Supernotes"], "modifies": { "pin": "read", From aff915fde9974c47938592b30ae5538fa7766ecf Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:51:13 -0700 Subject: [PATCH 12/24] Add Patreon link to script.json --- Dynamic Lighting Tool/script.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dynamic Lighting Tool/script.json b/Dynamic Lighting Tool/script.json index ad5e10669a..8b4be60ed6 100644 --- a/Dynamic Lighting Tool/script.json +++ b/Dynamic Lighting Tool/script.json @@ -5,8 +5,9 @@ "description": "### Dynamic Lighting Tool\r**Dynamic Lighting Tool** is a script that performs a host of Dynamic Lighting tasks. It allows you to change most Dynamic Lighting settings on tokens and pages in real time, without needing to call up dialog boxes. It also analyzes many common Dynamic Lighting issues and suggests solutions.\r\r\r### Features:\rA full report of all current relevant Dynamic Lighting information on the current page and the selected token.\rA *Why can't this token see?* button that performs a detailed analysis of the selected token and tries to pinpoint what the problem might be.\rA list of things to check manually that cannot be handled by mod scripts.\rPreset buttons for common lighting solutions.\rIdentifies controllers and represented sheet (if any) of selected token.\rExtensive hover text over most labels and settings with explanations and tips.\rIf the checkLightLevel script is installed (soon in One Click, or get it here), it will inform you of whether the token has light shining on it and how much.\r\r### Dependencies:\rThis script requires the following scripts for best performance. They will be loaded as dependencies\r**Token Mod.** This is used for setting token values. There are many idiosyncrasies in the Mod system for tokens, and the Aaron has already done the hard work of accounting for them.\r**checkLightLevel.** This adds functionality, but the script will function without it.\r\r### Command:\r`!dltool` or `!dltool --report`\r\rFor full documentation see [this thread](https://app.roll20.net/forum/post/11316788/script-dltool-a-dynamic-lighting-control-panel-and-troubleshooter) in the Roll20 forums.", "authors": "Keith Curtis", "roll20userid": "162065", - "dependencies": [ "TokenMod", "checkLightLevel" ], - "modifies": {}, + "patreon": "https://www.patreon.com/c/KeithCurtis", + "dependencies": [ "TokenMod", "checkLightLevel" ], + "modifies": {}, "conflicts": [], "previousversions": ["1.0.0","1.0.1","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9"] } From 8d1f757c6856e750840ee482a612ee0c64d1cc4d Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:51:26 -0700 Subject: [PATCH 13/24] Update script.json --- TokenHome/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TokenHome/script.json b/TokenHome/script.json index 1c0b479120..628c51320f 100644 --- a/TokenHome/script.json +++ b/TokenHome/script.json @@ -5,10 +5,11 @@ "description": "# TokenHome\n\nTokenHome is a GM-only Roll20 API script that allows tokens to store, recall, and manage multiple named \"home\" locations on the current page. Each location records pixel-precise X/Y coordinates and the token’s layer, enabling reliable repositioning, staging, and summoning workflows.\n\n---\n\n## Core Capabilities\n\n- Store multiple locations per token (L1, L2, L3, …)\n- Recall tokens to saved locations with layer restoration\n- Summon tokens toward a selected anchor based on proximity\n- Preserve compatibility with tokens outside page bounds\n- Automatically migrate legacy single-home token data\n- Clear individual locations or all stored data per token\n\n**Base Command:** `!home`\n\n---\n\n## Primary Commands\n\n```\n!home --set --L#\n!home --L#\n!home --summon [--L#] [--radius|#]\n!home --clear [--L#]\n!home --help\n```\n\n- `--set` stores the selected token’s current position and layer.\n- `--L#` recalls a token to a stored location.\n- `--summon` pulls tokens toward a selected anchor based on distance.\n- `--clear` removes stored location data from selected tokens.\n- `--help` opens the script’s help handout.\n\n---\n\n## Highlights\n\n- Unlimited numbered locations per token (case-insensitive).\n- Distance-based summoning can target a specific location or choose the closest.\n- Radius supports both pixel values and grid units.\n- Layer is restored on recall and summon.\n- Storage is embedded safely in GM Notes using a hidden JSON block.\n\nDesigned for GMs who want fast, reliable control over token positioning, staging, and recall without page locking or teleport hacks.", "authors": "Keith Curtis, based on a Script by the Aaron", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "graphic": "write" }, "conflicts": [], "previousversions": ["1.0.0","1.0.1"] -} \ No newline at end of file +} From 5d432ad02dc431442e4821f7123433db988f89c1 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:51:47 -0700 Subject: [PATCH 14/24] Update script.json with Patreon link and description Added Patreon link for support and updated script description. --- Supernotes/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Supernotes/script.json b/Supernotes/script.json index 0e83945f20..79ddd453ac 100644 --- a/Supernotes/script.json +++ b/Supernotes/script.json @@ -6,6 +6,7 @@ "authors": "Keith Curtis", "roll20userid": "162065", "dependencies": [], + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "state.Supernotes": "read, write", "graphic.represents": "read", From bb6b11214b978ded85898c5c957a5feddd7576d7 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:52:06 -0700 Subject: [PATCH 15/24] Add Patreon link to script.json Added Patreon link to the script metadata. --- Fade/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Fade/script.json b/Fade/script.json index db5cf3c857..c2400a1441 100644 --- a/Fade/script.json +++ b/Fade/script.json @@ -5,10 +5,11 @@ "description": "# Fade\n\nFade smoothly transitions graphics between 0% and 100% opacity over a specified time.\n\n---\n\n## Commands\n\n```\n!fade --in|\n!fade --out|\n!fade --in --all\n!fade --out --all\n```\n\n**** is optional (default: 1).\n**--all** affects all graphics on the current page.\n\n### Examples\n```\n!fade --out|5 → Fade selected graphics to 0% over 5 seconds\n!fade --in|3 → Fade selected graphics to 100% over 3 seconds\n!fade --in --all → Fade in all graphics on the page over 1 second\n```\n\n---\n\n## Features\n- All graphics fade simultaneously\n- Works on all layers\n- Ignores graphics already at target opacity\n- Silent operation (no chat spam)\n\n---\n\n## Usage Notes\n- If no graphics are selected, `--all` is required.\n- Page detection uses the player's last viewed page or the GM's active page.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "graphic": "write" }, "conflicts": [], "previousversions": [] -} \ No newline at end of file +} From fba9982cc2ad02d068fab9e6295ab345266bbacb Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:52:15 -0700 Subject: [PATCH 16/24] Add Patreon link to script.json Added Patreon link for support. --- Fix Turn Order/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Fix Turn Order/script.json b/Fix Turn Order/script.json index 69c989c457..cbb1dbfbff 100644 --- a/Fix Turn Order/script.json +++ b/Fix Turn Order/script.json @@ -5,6 +5,7 @@ "description": "# Fix Turn Order\n\nFix Turn Order is a GM-only Roll20 API script that helps clean up the Turn Order when it contains leftover token entries from other pages. This is a common issue when moving between maps and forgetting to clear the tracker.\n\n---\n\n## What It Does\n\nWhen run, the script checks the Turn Order and compares each token entry to the current player page. Any turns belonging to tokens that are not on the active page are listed in a clear chat report, grouped by the page they came from.\n\nFrom that report, the GM can:\n\n- Delete all off-page turns from a specific page at once\n- Delete individual off-page turns one by one\n\nNothing happens automatically. The script only runs when invoked, and no Turn Order entries are removed unless the GM clicks a button.\n\n---\n\n## What It Does *Not* Do\n\n- It does not monitor the game continuously\n- It does not remove turns for tokens on the current player page\n- It does not affect custom Turn Order items that are not tied to tokens (such as lair actions, round counters, or reminders)\n\n---\n\n## Usage\n\n**Base Command:** `!fixturnorder`\n\nRunning the command opens an interactive chat report with buttons to review and clean up off-page turns.\n\n---\n\nDesigned for GMs who frequently move between pages and want a quick, safe way to clean up forgotten Turn Order entries without touching custom or manual items.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "campaign": "read", @@ -12,4 +13,4 @@ }, "conflicts": [], "previousversions": ["1.0.0"] -} \ No newline at end of file +} From 07cab13952b1d400c86b30b73748047b28671f3c Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:52:30 -0700 Subject: [PATCH 17/24] Add Patreon link to script.json Added Patreon link for support and updates. --- JukeboxPlus/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/JukeboxPlus/script.json b/JukeboxPlus/script.json index a451fbe58e..0a783df3cf 100644 --- a/JukeboxPlus/script.json +++ b/JukeboxPlus/script.json @@ -5,6 +5,7 @@ "description": "# Jukebox Plus \n\nJukebox Plus lets you organize and control music tracks by **albums** or **playlists**. \nUse the toggle buttons in the sidebar to switch between views. Tracks are displayed on the right, and control buttons appear for each one. \n \n--- \n \n## Getting Started \n\nIssue the command `!jb` to create or refresh the Jukebox Plus handout. This is where all of the Jukebox Plus controls appear. \n \n--- \n \n## Header Buttons \n\nAt the top right of the interface: \n \n`Play All` `Together` `In Order` `Loop` `Mix` \n`Loop All` `Off` `On` \n`Stop All` `Find` `Help` \n \n### Button Descriptions \n \n**Play All** \n **Together** - Plays all visible tracks simultaneously. Limited to the first five visible. \n **In Order** - Plays all visible tracks one after the other. \n **Loop** - Plays all visible tracks one after the other, then starts over. \n **Mix** - Plays all looping tracks continuously, and all other tracks at random intervals. Use to create a custom soundscape. Stopped by `Stop All`. \n \n**Loop All** \n **Off** - Disables loop mode for all visible tracks \n **On** - Enables loop mode for all visible tracks \n \n**Stop All** - Stops all currently playing tracks. Also use to stop a Mix. \n**Find** - Search all track names and descriptions for the keyword. All matching tracks will be assigned to a temporary album called **Found**. You can then switch to the Found album to quickly view the results. To clear the results, simply delete the Found album using the Utility panel. \nIf you input \"d\" as the search term, it will create a temporary playlist of any duplicate tracks, grouped by name. \n**Help** - Displays this help page. Click **Return to Player** to return. \n \n--- \n \n## Sidebar: Navigation & Now Playing \n \n**View Mode Toggle** \n\nThe left sidebar lists all albums or playlists, depending on the current view mode. Clicking a name switches the view. \n \n`Albums` `Playlists` \n\nThese buttons let you switch between organizing by: \n **Albums** you define and tag yourself \n **Playlists** as defined in the Roll20 Jukebox system (not editable here) \n\nAt the bottom of the list: \n`Now Playing` - Filters the list to show only tracks currently playing. \n \n--- \n \n## Track Controls \n\nEach track shows these control buttons (these will be graphic buttons when viewed in the game): \n \n `play` **Play** - Start the track \n `loop` **Loop** - Toggle loop mode for the track \n `isolate` **Isolate** - Stops all others and plays only this one \n `stop` **Stop** - Stops this track \n `announce` **Announce** - Sends the track name and description to the chat window \n \n--- \n \n## Track Info and Management \n \n**Edit** - Click the track description \"edit\" link to create a description. \n\nDescription special characters: \n `---` inserts a line break \n `*italic*` uses single asterisks for italic text \n `**bold**` uses double asterisks for bold text \n `!d` or `!desc` includes the description when you Announce a track \n `!a` or `!announce` makes the track auto-announce on play \n \n**Tags** \nEach track has a Playlist tag and may have one or more Album tags. \n- `Playlist` tags are in blue \n- `Album` tags are in red \n\nClick `+ Add` to add an Album tag. \nClick a tag to jump to that Album or Playlist. \nClick the `x` on an Album tag to remove it: `Album name | x` \n \n**Image Area** \nClick the image area to enter either: \n- a valid image URL \n- a CSS color name (e.g. \"red\") \n- a hexadecimal color code (e.g. `#00ff00`) \n\nIf you provide an image URL, it will display beside the track name and in the chat on announce. \nIf you provide a color code, the square will show that color and use it when Announcing. \n \n--- \n \n## Utility Panel \n\nClick `Settings` to expand the utility tools. Includes: \n \n### Album Controls \n`Edit Albums:` `-` `+` `edit pencil icon` \nRename, add, or delete the currently selected album \n \n### Sorting \nA—Z: `albums` `tracks` \nAlphabetize Albums or the tracks within an Album \n \n### Mix Interval \n`reset` Resets to default mix intervals \n`min` Minimum seconds between tracks \n`max` Maximum seconds between tracks \nMix will choose a random time between these values for each interval. \n \n### Mode \nMode: `dark` `light` \nSwitch between dark and light interface modes \n \n### Refresh \n`Refresh` - Rebuilds the interface if something breaks \n \n### Backup \nBackup: `make` `restore` \n- Create or restore from backup handouts containing your album and playlist data \n- Use this to move data between games via the Roll20 Transmogrifier \n \n**Note:** Tracks are linked by ID, which changes between games. The script tries to match by name during restore, but renames and duplicates may cause mismatches. \n \n--- \n \n## Find \n\nUse the `!jb find keyword` command to search all track names and descriptions. \nMatching tracks are added to a temporary album called **Found**. \nDelete the Found album to clear the search results. \n \n--- \n \n## Useful Macros \n\nHere are some chat commands you can use in macros: \n \n- `!jb` - Show link to open the interface \n- `!jb play TrackName` - Play the named track \n- `!jb stopall` - Stop all currently playing audio \n- `!jb loopall` - Enable loop mode for visible tracks \n- `!jb unloopall` - Disable loop mode on all tracks \n- `!jb jump album AlbumName` - Switch to the given album \n- `!jb help` - Open this help screen \n- `!jb find keyword` - Search for keyword and assign matches to the \"Found\" album \n\nYou can also discover commands by pressing a control button, clicking in the chat window, and pressing the **Up Arrow** to see what was sent. \n", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "jukeboxtrack": "read, write" From 87bc74410a24ec67dbed8e019f5971f427c0c58b Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:53:01 -0700 Subject: [PATCH 18/24] Add Patreon link and reorder dependencies --- Condefinition/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Condefinition/script.json b/Condefinition/script.json index 34f98102d7..7b2a1a7f64 100644 --- a/Condefinition/script.json +++ b/Condefinition/script.json @@ -5,7 +5,8 @@ "description": "## Condefinition\rWorks with Condition Sync on the D&D 2024 sheet.\r\r### What it does\r\rCondefinition reads incoming roll templates and looks for imposed conditions and saving throws. It displays button(s) in chat that allow you to:\r- Quickly apply a corresponding token marker\r- Define the condition\r- Track concentration\r- Roll saving throws\r- Apply damage from spells or effects triggered by saving throws\r- If the roll template contains a saving throw, it gives a button for the saving throw. Press the button and click on a target to roll the appropriate save. With **GroupCheck** installed, it runs a groupcheck save. If you have [**Apply Damage**](https://app.roll20.net/forum/post/3631602/script-groupcheck-roll-checks-saves-et-cetera-for-many-tokens-at-once/?pageforid=4359153#post-4359153) installed, it also applies damage.\r- If the roll template contains an imposed condition, it creates a button to define the condition. If there's a cross-reference in the definition, it creates an inline link. You can hover or whisper/broadcast the text to players.\r- You can toggle that condition on selected tokens. Assigning token markers requires **TokenMod**, and for improved function, **LibTokenMarkers**. Default status markers are included, and the configuration handout allows you to customize these.\r- If installed via One-Click, this Mod will also auto-install **GroupCheck**, **TokenMod**, and **LibTokenMarkers** if they’re missing. **[Apply Damage](https://app.roll20.net/forum/post/3631602/script-groupcheck-roll-checks-saves-et-cetera-for-many-tokens-at-once/?pageforid=4359153#post-4359153)** must be installed manually.\r- In addition to official conditions, it tracks many common features that benefit from markers (e.g., Raging, Hunter's Mark, Hexblade's Curse).\r- The Concentration condition marker also tracks concentration and prompts for a Constitution Save when damaged.\r- Compatible with Classic and Jumpgate VTT, and the 2014 or 2024 D&D sheets. Advanced configuration requires JavaScript knowledge.\r### What it can't do\r\rThere are some limitations:\r- Primarily designed for GMs and NPCs. Players may see buttons, but full use is GM-facing.\r- Conditions are matched to tokens, not character sheets (intentional, for individual instances like multiple goblins).\r- Some condition wordings may be missed, especially in non-WotC content.\r- Currently, all buttons and definitions are whispered to the controller of the NPC. If needed, this could change based on whisper settings.\r### Helper Scripts\r- **Required:** `TokenMod`, `libTokenMarkers`\r- **Optional/Recommended:** `GroupCheck`, [`Apply Damage`](https://app.roll20.net/forum/post/3631602/script-groupcheck-roll-checks-saves-et-cetera-for-many-tokens-at-once/?pageforid=4359153#post-4359153)\r---\r## Operation\rFor full documentation and configuration options, enter `!config-help` or `!condef-config` in chat. The script produces a full documentation handout.", "authors": "Keith Curtis", "roll20userid": "162065", - "dependencies": [ "TokenMod", "GroupCheck", "libTokenMarkers" ], + "patreon": "https://www.patreon.com/c/KeithCurtis", + "dependencies": [ "TokenMod", "GroupCheck", "libTokenMarkers" ], "modifies": {}, "conflicts": [], "previousversions": ["1.0.0", "1.0.1", "1.0.2", "1.0.3"] From 8d0f0ad7090fc316256770143fab569fd2bbc9fa Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:53:15 -0700 Subject: [PATCH 19/24] Add Patreon link to script.json Added Patreon link to support the script's development. --- Token-Action-Maker/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Token-Action-Maker/script.json b/Token-Action-Maker/script.json index ebacb8f292..fbc26f4e03 100644 --- a/Token-Action-Maker/script.json +++ b/Token-Action-Maker/script.json @@ -5,6 +5,7 @@ "description": "*By keithcurtis, based on original code by kevin, with assitance and additions by Oosh, GiGs, and bretmckee*\n\nThis script creates token actions on selected tokens for the D&D 5e by Roll20 sheet. Tokens must represent character sheets, either PC or NPC.\n\n> *0.2.9, the script will also abbreviate common phrases like '(One Handed)' to '-1H'.*\n> \n> *0.3.3, the ability to protect specific token actions was added (put a period after the name).*\n> \n> *0.3.4, added support for the new npc bonus action repeating field.*\n> \n> *0.3.5, numerous fixes*\n> \n> *0.3.6, Added support for Pathfinder 2 by Roll20 Sheet. Oosh provided better function to allow saves and checks to account for global modifiers*\n\n**!ta** This command will create a full suite of token action buttons for a selected character. Actions for NPCs and Attacks for PCs.\n\n**!sortta** This command functions identically to !ta, but will prepend 'a-' to NPC actions, and 'la-' to NPC Legendary Actions. This is for users who like to alphebetize Token Actions. This is not recommended for the PF2 sheet, as it breaks the logical progression of Attack-Attack2-Attack3.\n\n**!deleteta** will delete unprotected token actions for the selected character. To protect a token action, end its name with a period. 'longsword' will be deleted. 'longsword.' will not. This allows you to keep any custom token actions from being affected by the script.\n\n**!deleteallta** will delete ALL token actions for the selected character, whether they were created by this script or not. Use with caution.\n\n## D&D 5th Edition by Roll20 Sheet\nYou can create specific classes of abilities by using the following arguments separated by spaces:\n- **attacks** Creates a button for each attack. In the case of NPCs, this includes all Actions. (PC/NPC)\n- **trait**s Creates a button for each trait. PCs can have quite a number of these, so it is not recommended to run this command on PCs. (PC*/NPC)\n- **pc** creates full suite of buttons for everything but traits. Although this will also work on npcs, the intent is to not include trait buttons for pcs, which can get rather numerous. \n- **bonusactions** Creates a button for each bonus action. This will be ignored on PCs since only NPC sheets have a repeating attribute for bonus actions. (NPC)\n- **reactions** Creates a button for each reaction. This will be ignored on PCs since only NPC sheets have a repeating attribute for reactions. (PC)\n- **spells** Creates a button that calls up a chat menu of all spells the character can cast. (PC/NPC)\n- **checks** Creates a drop down menu of all Ability and Skill (Ability) checks. Recommended for NPCs, as PC checks and Saves can be affected by many different abilities as levels progress, that this script cannot account for. (PC*/NPC)\n- **saves** Creates a dropdown menu of all saving throws. Recommended for NPCs, as PC checks and Saves can be affected by many different abilities as levels progress, that this script cannot account for. (PC*/NPC)\n- **init** Creates a button that rolls initiative for the selected token (PC/NPC)\n- **name** Normally, Token Actions are created using the character_id. They will still function even if the character is renamed. However this is not always desireable. If a character is moved to a new game via the Character Vault, it will receive a new character_id, and the token actions will not function. If you intend to move the character, use the 'name' argument in the string and it will call the token actions by name.\n- **help** Calls up this help documentation\n\nExamples:\n\n**!ta saves checks** will create token ability buttons for Ability Checks and Saving Throws.\n\n**!ta name** will create alltoken ability buttons and identify them by name, rather than character_id.\n\n## Pathfinder Second Edition by Roll20 Sheet\nAll PF2 use requires adding the argument 'pf2' to the argument list. Otherwise the script will try to create Token Actions for the 5e sheet. Until all sheets have a uniform sheet identifier attribute, this is necessary. In cases where there is an action cost, it will be indicated in the button name as `Action<#>`.\nYou can create specific classes of abilities by using the following arguments separated by spaces:\n- **pf2** *Required on all PF2 commands*\n- **attacks** Creates a button for each attack. TAM will append a '-M' or '-R' after the name to distinguish melee from ranged. Each Attack will have a two buttons immediately following for Attack 2 and Attack 3. These will be abbreviated using the first two characters from each word in the Attack. Example `Silver Dagger` `SiDa-2` `SiDa-3` (PC/NPC)\n- **reactive** Creates a button for each reaction (NPC)\n- **offensive** Creates a button for each offensive ability (PC/NPC)\n- **spells** Creates a button that calls up a chat menu of all spells the character can cast. These are separated by innate, focus, cantrips and normal spells. Normal Spells are separated by level. (PC/NPC)\n- **actions** Creates a button for each normal action (NPC)\n- **checks** Creates a drop down menu of all Skill check (PC/NPC)\n- **saves** Creates a dropdown menu of all saving throws (PC/NPC)\n- **init** Creates a button that rolls initiative for the selected token, obeying the skill chosen on the character sheet. The skill cannot be chosen without API interaction, so it will need to be manually chosen. (PC/NPC)\n- **name** Normally, Token Actions are created using the character_id. They will still function even if the character is renamed. However this is not always desireable. If a character is moved to a new game via the Character Vault, it will receive a new character_id, and the token actions will not function. If you intend to move the character, use the 'name' argument in the string and it will call the token actions by name.\n\nExamples:\n\n**!ta pf2** will generate a full suite of token actions For PCs, this would be the same as typing `!ta pf2 checks saves attacks offensive reactive interaction spells`. For PCs, this would be the same as typing `!ta pf2 checks saves attacks offensive spells`.\n\n**!ta pf2 saves checks** will create token ability buttons for Skill Checks and Saving Throws.\n\n**!ta pf2 name** will create all token ability buttons and identify them by name, rather than character_id.", "authors": "Keith Curtis, Kevin, Bret", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "ability.*": "read, write" }, From e72df76fa62bbc538a327c89dff1bda0da691e18 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:53:33 -0700 Subject: [PATCH 20/24] Add Patreon link to script.json Added Patreon link for support and contributions. --- PinTool/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PinTool/script.json b/PinTool/script.json index 581b64da7b..2fdab21be4 100644 --- a/PinTool/script.json +++ b/PinTool/script.json @@ -6,10 +6,11 @@ "authors": "Keith Curtis", "roll20userid": "162065", "dependencies": [], + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "graphic": "write", "pin": "write" }, "conflicts": [], "previousversions": ["1.0.0","1.0.1","1.0.2","1.0.3","1.0.4"] -} \ No newline at end of file +} From a6aa651697e0edde5e9018a303ab1b0dc7b521eb Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:53:52 -0700 Subject: [PATCH 21/24] Add Patreon link to script.json Added Patreon link for support and contributions. --- PinNote/script.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PinNote/script.json b/PinNote/script.json index 1864e46913..b024b164ab 100644 --- a/PinNote/script.json +++ b/PinNote/script.json @@ -5,6 +5,7 @@ "description": "# PinNote\n\nPinNote sends information from linked or custom map pins to chat using any Supernotes template. Supernotes must be installed for this script to function.\n\n---\n\n## Arguments\n\nArguments are case-insensitive and use the format:\n\n```\n--key|value\n```\n\n---\n\n### --to|\n\nControls where the message is sent.\n\n#### --to|pc\n\nSends a public message to chat.\n\n- GM notes are never included.\n\n---\n\n#### --to|gm\n\nWhispers the message to the GM only.\n\n- GM notes are included.\n\n---\n\n#### --to|self\n\nWhispers the message to the invoking player.\n\n- GM notes are included only if the invoker is a GM.\n\n---\n\nIf a non-GM runs the command, --to is ignored and treated as pc.\n\n---\n\n### --template| (optional)\n\nSelects a Supernotes display template.\n\n- If omitted or invalid, the generic template is used silently.\n\n---\n\n## Examples\n\n```\n!pinnote\n!pinnote --to|gm\n!pinnote --to|self --template|dark\n!pinnote --template|wizard\n```\n\n---\n\n## Requirements\n\n- Exactly one map pin must be selected.\n - If none are selected, the script reports an error.\n - If multiple are selected, only the first pin is used.\n\n- The pin may be a linked pin or a custom pin.\n - If linked to a handout, the script pulls the relevant section from the handout.\n - If custom, the script uses the Notes field of the pin.\n\n- A custom pin must contain notes.\n - If the Notes field is empty, nothing is sent and an error is shown.\n\n- Supernotes must be installed.\n - If missing, the script exits and notifies the GM.\n\n---\n\nType **!pinnote** in chat to use the script.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": ["Supernotes"], "modifies": { "pin": "read", @@ -12,4 +13,4 @@ }, "conflicts": [], "previousversions": ["1.0.0","1.0.1"] -} \ No newline at end of file +} From ffd321d4dadc4d4f378f60b3b7311f5b9cc2918f Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:54:17 -0700 Subject: [PATCH 22/24] Add Patreon link to script.json --- ImageEditor/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/ImageEditor/script.json b/ImageEditor/script.json index 486fa303a7..2c84c30e10 100644 --- a/ImageEditor/script.json +++ b/ImageEditor/script.json @@ -5,6 +5,7 @@ "description": "Provides a graphical interface for editing images inside Roll20 handouts, including layout, styling, and attributes without manual HTML editing.", "authors": "Keith Curtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "modifies": { "handout": "read,write" }, From 25c2515d5fba08655ffd3736d6de2451e56f9659 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:54:29 -0700 Subject: [PATCH 23/24] Update script.json --- Portal/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Portal/script.json b/Portal/script.json index fbe9aba133..94feef03e5 100644 --- a/Portal/script.json +++ b/Portal/script.json @@ -5,6 +5,7 @@ "description": "# Portal\n\n**Portal** is a script for converting older dynamic lighting maps to modern doors and windows.\nIt also allows for bulk setting of door and window properties", "authors": "keithcurtis", "roll20userid": "162065", + "patreon": "https://www.patreon.com/c/KeithCurtis", "dependencies": [], "modifies": { "door": "read, write", From 8815007441f6b8fc372a10a34631a31b319bbb49 Mon Sep 17 00:00:00 2001 From: keithcurtis1 Date: Mon, 18 May 2026 15:54:33 -0700 Subject: [PATCH 24/24] Add Patreon link to script.json Added Patreon link for support and funding. --- Faces/script.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Faces/script.json b/Faces/script.json index 5a9bcb892b..9bedc4dfd1 100644 --- a/Faces/script.json +++ b/Faces/script.json @@ -4,6 +4,7 @@ "version": "0.1.2", "description": "**Faces** is a script is designed to provide a quick visual interface for swapping token images and names, drawing the information from a Rollabel Table or an existing token. The user can choose which face to apply from the graphical list, or apply random faces to a group of selected tokens. It can also quickly create rollable tables from an existing set of tokens, both simple and multisided. The interface will work for both players and GMs.\r\rThere is a comprehensive help system accessible by typing `!faces help` in the chat window, once the script is installed. There is also documentation and feedback available from the [Faces Feedback Thread](https://app.roll20.net/forum/post/11129007/script-faces-1-dot-0) in the Roll20 Forums.\r\r**What this script does not do:**\r- Adjust token size.\r- Change any token-character representation, linking, or attributes.\r- It is not intended to be a Wildshape script. There are much better-suited scripts for that.\r\r\r**Potential uses for this script:**\r- Disguise-master characters\r- Changeling characters\r- Alter Self and Seeming spells (and similar)\r- Providing the GM a way to alter the image choices for a group of enemies. Quickly differentiate between goblin archers, goblin infantry, dead goblins\r- Give a visual interface for a player who has multiple expressions or variations for their character\r- Characters who contract lycanthropy\r- Monsters with different states: Ankheg on surface, surfacing or a Bugs Bunny-style burrowing mound\r- Build a library of townsfolk, scholars, city guards or other commoner-types.\r- Assigning map pin icons: Tavern vs temple vs shop, etc.\r- Quickly changing the state of a trap image\r- Changing a closed door image to an open door image.\r- Laying out a bunch of map tiles and quickly switching between them to build a dungeon\r- having tables for map furnishings: a chair table, a door, even a table table.\r- Change downed enemies to a creative variety of grisly remains?\r- A GM could present players with a menu of tokens for their PCs\r- A rollable table of scenes and images to show characters using shift-Z. Just keep one token on the VTT and switch at need.\r- Quickly create a rollable table from a bunch of uploaded images.\r- Move image from one token to another (select first token, use !faces existing, and use the resulting button to assign to one or more other tokens)\r- Randomly populate a scene with differentiated token images\r- Probably more I haven't thought of.", "authors": "Keith Curtis", + "patreon": "https://www.patreon.com/c/KeithCurtis", "roll20userid": "162065", "modifies": { "ability.*": "read, write"