Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Macros to save text for a token and then call upon it later

Hey there, this should be a pretty simple one for you all. I just don't know how to do it myself.  I have ChatSetAttr, and I want to create two macros. One to ask me for text to put in as an attribute for a token, and the other to whisper that text to me. Basically, what I'm looking to do is to write a basic 'game plan' for certain monsters, save it, and remind myself later what their plan looks like. Can you help?
1728926735

Edited 1728926841
It sounds to me like what you're looking to do could be handled via the GM Notes section on all tokens and handouts.  Is there a reason you don't want to do use that section? 
That would be fine if it could be whispered to me once I write it in there. Maybe a global macro that appears on all tokens for me?
1728928303
timmaugh
Pro
API Scripter
If this is the direction you want to go, look at the Supernotes script. You're basically describing exactly what it does.
// API Script to Whisper GM Notes from a Selected Token on("ready", function() { log("Whisper GM Notes script loaded."); on("chat:message", function(msg) { // Check if the command is from the API and is the correct command if (msg.type === "api" && msg.content.startsWith("!whisperGMNotes")) { var selectedTokens = getSelected(msg); if (selectedTokens.length === 0) { sendChat("System", "Please select a token."); return; } var token = selectedTokens[0]; // Retrieve the GM notes from the token var gmNotes = token.get("gmnotes"); // Decode the HTML if GM notes exist if (gmNotes) { var decodedNotes = decodeURIComponent(gmNotes); sendChat("Whisper", `/w ${msg.who} ${decodedNotes}`); } else { sendChat("System", "No GM notes found for the selected token."); } } }); }); // Helper function to get selected tokens function getSelected(msg) { if (msg.selected) { return msg.selected.map(function(o) { return getObj("graphic", o._id); }).filter(function(obj) { return obj !== undefined; }); } return []; } Create this API script in your library. From there, create the following token macro: !whisperGMNotes From there, you can select a token and click the macro, it will whisper you the contents of the GM notes for that token
1728931614

Edited 1728931807
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Supernotes is perfect for reading the note, it handles hidden text, can read from multiple sources, can whisper or send openly. It does not set notes, however. I would suggest this script that I believe the Aaron wrote for me long ago code below) Syntax !set-gmnote any text here Will put the text " any text here "  into a token's gmnote !set-cgmnote will do the same for the gmnotes on a character !set-bio will do the same for a character's bio notes. To handle multiple paragrpahs, wrap the message in double curly braces, like in a roll template: !set-gmnote {{Paragraph 1 Paragraph 2}} You can alse replace the word "set" with "add" in the macro and it will append the information to any existing note instead of replacing the entire contents. Code on('ready',function(){     'use strict';     const cmdregex=/^(!(?:set|add)-(?:c?gmnote|bio))\s*/,         formatMsg = (c)=>c.replace(cmdregex,'').replace(/(^{{\s*|\s*}}$)/gm,'');     on('chat:message',function(msg){         if('api' === msg.type) {             let match=msg.content.match(cmdregex);             if(match && playerIsGM(msg.playerid) ){                 let content = formatMsg(msg.content),                     who=(getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname');                 if(_.has(msg,'inlinerolls')){                     content = _.chain(msg.inlinerolls)                         .reduce(function(m,v,k){                             var ti=_.reduce(v.results.rolls,function(m2,v2){                                 if(_.has(v2,'table')){                                     m2.push(_.reduce(v2.results,function(m3,v3){                                         m3.push(v3.tableItem.name);                                         return m3;                                     },[]).join(', '));                                 }                                 return m2;                             },[]).join(', ');                             m['$[['+k+']]']= (ti.length && ti) || v.results.total || 0;                             return m;                         },{})                         .reduce(function(m,v,k){                             return m.replace(k,v);                         },content)                         .value();                 }                 if(content.length){                     let tokens = _.chain(msg.selected)                         .map( s => getObj('graphic',s._id))                         .reject(_.isUndefined)                         ;                     switch(match[1]){                         case '!set-gmnote':                             tokens.each( o => {                                 o.set({                                     gmnotes: content                                 });                             });                             break;                         case '!set-cgmnote':                             tokens.map( o => getObj('character',o.get('represents')))                                 .reject(_.isUndefined)                                 .each( o => {                                     o.set({                                         gmnotes: content                                     });                                 });                             break;                                                      case '!set-bio':                             tokens.map( o => getObj('character',o.get('represents')))                                 .reject(_.isUndefined)                                 .each( o => {                                     o.set({                                         bio: content                                     });                                 });                             break;                         case '!add-gmnote':                             tokens.each( o =>{                                 o.set({                                     gmnotes: `${o.get('gmnotes')}<br>\n${content}`                                 });                             });                             break;                         case '!add-cgmnote':                             tokens.map( o => getObj('character',o.get('represents')))                                 .reject(_.isUndefined)                                 .each( o => {                                     o.get('gmnotes',(notes)=>{                                         _.defer(()=>o.set({                                             gmnotes: `${notes}<br>\n${content}`                                         }));                                     });                                 });                             break;                         case '!add-bio':                             tokens.map( o => getObj('character',o.get('represents')))                                 .reject(_.isUndefined)                                 .each( o => {                                     o.get('bio',(notes)=>{                                         _.defer(()=>o.set({                                             bio: `${notes}<br>\n${content}`                                         }));                                     });                                 });                             break;                     }                 } else {                     sendChat('',`/w "${who}" <div>`+                         `<div>Use <code>COMMNAD Some Text</code> or <code>COMMAND {{ some multi-line text}}</code>.</div>`+                         `<div><b>Commands:</b></div>`+                         `<div><ul>`+                             `<li><code>!set-gmnote</code> -- Replaces contents of token gmnotes.</li>`+                             `<li><code>!set-cgmnote</code> -- Replaces contents of character gmnotes.</li>`+                             `<li><code>!set-bio</code> -- Replaces contents of character bio.</li>`+                             `<li><code>!add-gmnote</code> -- Appends to contents of token gmnotes.</li>`+                             `<li><code>!add-cgmnote</code> -- Appends to contents of character gmnotes.</li>`+                             `<li><code>!add-bio</code> -- Appends to contents of character bio.</li>`+                         `</ul></div>`                     );                 }             }         }     }); });
1728931731

Edited 1728934688
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
To use the above in a macro, I would suggest: !set-gmnote {{?{Set text here}}} This will prompt you for the text when you run the macro. EDIT: I would suggest only using single parapgraph entries. Something about the way that the Set script interacts with supernotes causes a linebreak issue, and I don't have the time to investigate right now. For single line entries it works great. EDIT TO EDIT: replacing every instance of <br>/n with <br> in the script above should fix the add function. Further investigation needed for the {{}} multiple paragraphs.
Your script seems to be working just fine for now. I appreciate the hard work. This was way more than I expected :)
It’s been a while, but in the interests of saving time, I know there’s a tooltip section for each token. Is there a way to quickly edit that via chat in the same fashion? If it’s a bother, I will totally understand.
1733152353
timmaugh
Pro
API Scripter
That would require TokenMod to write the change, and the MetascriptToolbox to read it (if necessary). !token-mod --set tooltip|Honky Sets it. !The selected token's tooltip reads @(selected.tooltip). {&simple} Would show in chat: The selected token's tooltip reads Honky. And you could use the metascript construction in TokenMod so you could effectively append to the current tip: !token-mod --set tooltip|"@(selected.tooltip) Chonky" Would append " Chonky" to the current selecte token's tooltip.
timmaugh said: That would require TokenMod to write the change, and the MetascriptToolbox to read it (if necessary). !token-mod --set tooltip|Honky Sets it. !The selected token's tooltip reads @(selected.tooltip). {&simple} Would show in chat: The selected token's tooltip reads Honky. And you could use the metascript construction in TokenMod so you could effectively append to the current tip: !token-mod --set tooltip|"@(selected.tooltip) Chonky" Would append " Chonky" to the current selecte token's tooltip. Is there a way to turn it into a macro along the lines of what @Keithcurtis said earlier in the thread, where it asks what I want to input? Or not so much?
1734016423
timmaugh
Pro
API Scripter
Absolutely! Keith used a roll query to get the input. Since those resolve before the message reaches the scripts, you can use that where you want to store the information: !token-mod --set tooltip|"?{Enter text for tooltip}" You can see I closed it in quotation marks because I believe that is the way that TokenMod keeps all of it together and recognizes it as a single thing, should you intend to include spaces in your response. =D (For more information on the order different Roll20 constructions are resolved, you can check out this video I put together. Really, it's the first 15 minutes that deal with the timing of standard stuff, then the back portion deals with metascripts... so watch that, but watch it when you're ready to do more with metascripts.)
1734016631
timmaugh
Pro
API Scripter
Oh, and whispering would mean including the "/w gm" (or other target) at the beginning of the line that reads the tooltip: !/w gm @(selected.tooltip)
1734030829
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
This video from Nick Olivo might help. It just came up in my feed again this morning. <a href="https://youtu.be/iE2u0XWmvpw?si=Ksdvnl7QJoW-dmNx" rel="nofollow">https://youtu.be/iE2u0XWmvpw?si=Ksdvnl7QJoW-dmNx</a>