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

GM Notes - For players to read....

1556895914

Edited 1557231045
Hi All, I'm currently running through a campaign which travels through a few large towns, one of which is Skullport . Filled with lots of locations and simple non combat encounters. The players don't want to just skip things out for speed, but they also don't want to wander around for multiple sessions as a big group, picking up tid-bits of info here and there... so my plan is to ask the party to split up, and each player go and visit 3-4 locations each. - Then regroup 'At dinner' and through roleplay, reveal what they have learned to the group.... Hopefully they will start finding connections between the locations "You mentioned that the man was found dead with a stab wound? In my location, I spotted a young woman throwing a knife into the river, I thought she was suspicious!" I have already written up the encounters and included requests for certain rolls etc. - And I want to find a cool way to show them to the players 'quietly' when asked. My current solution which (will probably be fine) is to have each location as a handout. - Which I will quietly give viewing access and reveal as each player asks for them. - But I wanted to know if I could use the GMNotes API in some way. I was thinking of making 30 tokens (one for each location) and placing them all over the map. Then the players can click a location and it will then shout out a phrase "BOB is visiting the 'Crones Nest'" - It will then whisper to the player who clicked it.... a description of the location, taken from the tokens 'notes' field. I know that there might be a way to pop the text into the 'Max_bar2' and call for that, but to be honest - I'm not sure whether that's the best way to do it? Especially if I want to include a few line breaks, paragraphs etc. Is this something that 'GMNotes' is already able to do? Thanks!
1556896877
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
I have a separate script modified from GMNotes (probably easy to do internally, but I suck at js). I changed the command from !gmnote to !pcnote, and edited the output line to get rid of the "/w gm".
Hi Keith, Sounds like a good idea. I really appreciate all your help! Does this mean that your version displays the text to ALL players, not just the one that 'called it'. I presume that the players have to be able to select the token in question (as apposed to just ' target ' it), meaning that the players need to have edit access?
1556904166

Edited 1556904532
The Aaron
Roll20 Production Team
API Scripter
This might work for you: /em is visiting @{target|token_name}! !inspect @{target|token_id} Script: on('ready',() => { on('chat:message',(msg) => { if('api' === msg.type && msg.content.match(/^!inspect/) ){ let content = msg.content.split(/\s+/); let token=getObj('graphic',content[1]); if(token){ let who = getObj('player',msg.playerid).get('displayname'); sendChat('',`/w "${who}" <div style="border:1px solid #999;border-radius:.5em;padding:.1em .5em; font-size: .6em;background-color:#eee; color:#333;">${unescape(token.get('gmnotes')).replace(/(?:[\n\r]+|<br\/?>)/g,'<br>')}</div>`); } } }); });
1556912384

Edited 1556912406
Oh my god it's amazing :) It works perfectly. Thank you Thank you Thank you
1556912449
The Aaron
Roll20 Production Team
API Scripter
=D  No problem. =D
1556924684

Edited 1556924722
So... I've done a bit more with it now. I've made an ability for each player character that reads: /em investigates @{target|Please click on a 'Pin' on the map|token_name}! !inspect @{target|Please click on a 'Pin' on the map|token_id} When they click it, they have to pick the thing they're investigating.. - In this case the little red 'pin' icon. This then spits out whatever is in the 'Pin Tokens' 'Notes' field, in my case it reads like this: I've also been able to pre-write a button to request a skill roll. Normally, I can't make a button requesting a skill roll, unless I already know who's going to be pressing the button in advance.... - (Since the button has to know the 'users' stats (like wisdom modifier etc) beforehand). - But this way, using the API, I seem to be able to get away with it... NOTES FIELD As you enter the cavern, you see that it is lit by  sparkling stones  in the ceiling. It almost gives the impression of a twinkling nights sky... You can still hear the feint sound waves  splashing around  outside but is quiet in the room itself... almost too quiet! Could you please make a  [Perception Check](~@{target|Please click your own token}|Perception) In this case... a perception check. Regardless of who presses it, it then asks... Once the user just picks their own token.... I'm pretty happy with that. Cheers again.
1556928160
The Aaron
Roll20 Production Team
API Scripter
Very cool!  I'm glad it's working out!
1556947491
GiGs
Pro
Sheet Author
API Scripter
It might be an idea to add some kind of check, to make sure that players cant use this to check GM Notes of any character on the map. It's something they might even do accidentally. Maybe something in the inspect script that checks to see if a ShowPlayers stat (for example) exists, and is set to 1, yes, or true, and if not, whisper an alternative message, something like: "you can inspect that, you naughty goblin". Then link all your tokens to be revealed to players to this one character.  Or.... maybe it checks to see if the token is linked to a character, and only reveals the text if it no link exists.  Or link it to a token bar, so the GM can quickly flip whether something should be hidden or revealed as a session proceeds. Lots of possibilities. Maybe the last one is most practical and simplest.
1556947606
GiGs
Pro
Sheet Author
API Scripter
Godthedj, Have you tried using selected here? Could you please make a  [Perception Check](~@{target|Please click your own token}|Perception)
Yes, I realised the same after I'd turned my PC off last night. I'll be changing it to this when back online.
GiGs. - Ref players sneaking a look at character notes... Yes good point. I think the text is taken from the token note field. Off the top of my head, I can't even remember is tokens have a GM Notes field too. I don't typically save any important hidden information in TOKEN notes field. It's usually saved in the CHARACTER notes fields instead. 
1556963711
The Aaron
Roll20 Production Team
API Scripter
It is taken from the token notes field, which is a GMNotes field. If I were going to add a check, I'd suggest something like having bar1 contain "inspectable" or some such. 
To be honest. I've never used the TOKEN notes field for anything other than for this exercise. I personally don't need such a feature. That said... it's not a bad shout!
1556971480
The Aaron
Roll20 Production Team
API Scripter
It would be an easy change: if( token && 'inspectable' === token.get('bar1_value') ) {
Hey @Aaron. I'm at your mercy once again! I recently learned that I can make buttons that can generate new lines of text quite easily! The magic bit of code being !
 (Which I think is the equivalent of a 'Carriage Return' /desc Would you like to examine the [window](!
/w gm Info about the window here), [dresser](!
/w gm Info about the dressed here) or [Pile of torn clothes](!
/w gm Info about the torn clothes here)? This would allow me pre-generate little descriptions which I can send straight to players as text, or have it whispered the GM. - It works quite nicely. However, since my experimenting with buttons worked so well earlier (see earlier comments), I thought I'd try combining this with the GM Notes field... ..However... when I press this set of fancy buttons... they unfortunately don't do anything anymore :( - Presumably because they've been generated via the API. Any Ideas?
1557140801
The Aaron
Roll20 Production Team
API Scripter
Hmm. I'll have to take a look. I bet Keith knows though, he does that sort of thing all the time...
change the  !
 to ` see if that works?
1557143080
The Aaron
Roll20 Production Team
API Scripter
Actually, I sorted it out.  Use this version and it should work: on('ready',() => { on('chat:message',(msg) => { if('api' === msg.type && msg.content.match(/^!inspect/) ){ let content = msg.content.split(/\s+/); let token=getObj('graphic',content[1]); if(token){ let who = getObj('player',msg.playerid).get('displayname'); sendChat('',`/w "${who}" <div style="border:1px solid #999;border-radius:.5em;padding:.1em .5em; font-size: .6em;background-color:#eee; color:#333;">${ unescape(token.get('gmnotes')) .replace(/(?:[\n\r]+|<br\/?>)/g,'<br>') .replace(/!([&]|[&]amp;)#13;/g,'!\r') }</div>`); } } }); }); The problem was the & getting turned into &  This version will fix that for API command buttons.
1557152359
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
The script Aaron provided above formats its own output. The notes script I use puts it into a Roll Template, which is less universal, but will carry over returns and most formatting.
1557336604

Edited 1557336622
Elemental Flame
Pro
Marketplace Creator
Everything you have done here is brilliant. I am now in a race to overhaul the portion of my campaign my players are in before we play on Friday to allow my players to 'Investigate' NPCs and important plot items so they can get a picture and a brief description. This is working like a charm so far. Something mentioned earlier got me thinking. I only just learned about the /desc command in this thread. Would it be possible to use this API, or something similar, so I can put a token on the GM layer and then use a command like !DescribeIt inside a macro to click the token and then post what is in the GM notes in chat as a description? I'd love to put a hidden token in each room that I can click and post a description for my players when they enter.
1557339115
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
I do this by having a second copy of the script, where I edit "/w gm" to "/desc", and change the !note to another command, say, "!pcdesc" I got really fancy after than and edited in a footer with "[Send to players](!pcdesc)" on a new line at the end of the template. But it could be done with just a different macro. I have been mercilessly bugging The Aaron to write the Ultimate Notes Script, where source and output and template can all be controlled by one script. But he only has so much time for me to steal...
1557341461
Elemental Flame
Pro
Marketplace Creator
I'm terrible at reading these scripts, but I think the trigger command in the one I am using is !inspect, not !note. Would my butchering of the script below achieve what you're saying with the !pcdesc command? on('ready',() => { on('chat:message',(msg) => { if('api' === msg.type && msg.content.match(/^!pcdesc/) ){ let content = msg.content.split(/\s+/); let token=getObj('graphic',content[1]); if(token){ let who = getObj('player',msg.playerid).get('displayname'); sendChat('',`/desc ${ unescape(token.get('gmnotes')) .replace(/(?:[\n\r]+|<br\/?>)/g,'<br>') .replace(/!([&]|[&]amp;)#13;/g,'!\r') }</div>`); } } }); }); I can't even picture what the footer thing you're talking about does but I am all for fancy things.
1557342042
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
I'm going to let The Aaron field that. It's very different in structure from the script I modified.
1557349143
The Aaron
Roll20 Production Team
API Scripter
Looks like it will work like you want. =D
1557437299
Elemental Flame
Pro
Marketplace Creator
The Aaron, would TokenMod work to set the gmnotes on a token? I'm not seeing it listed as an option in your post providing instructions on the script and I'm not having any luck in my game using !token-mod --set gmnotes or !token-mod --set notes.
1557443941
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Here's the one I use, written by the Great and Powerful Scriptomancer.
1557446775
The Aaron
Roll20 Production Team
API Scripter
I really should roll that into TokenMod...
I'm sorry, I feel bad for all the questions here! I currently use a 'GM notes' with the following script: on('ready',()=>{ const blockElements = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'pre', 'address', 'blockquote', 'dl', 'div', 'fieldset', 'form', 'hr', 'noscript', 'table','br' ]; const rStart=new RegExp(`<\\s*(?:${blockElements.join('|')})\\b[^>]*>`,'ig'); const rEnd=new RegExp(`<\\s*\\/\\s*(?:${blockElements.join('|')})\\b[^>]*>`,'ig'); const getLines = (str) => (rStart.test(str) ? str .replace(/[\n\r]+/g,' ') .replace(rStart,"\r$&") .replace(rEnd,"$&\r") .split(/[\n\r]+/) : str .split(/(?:[\n\r]+|<br\/?>)/) ) .map((s)=>s.trim()) .filter((s)=>s.length) ; const cmdRegex = /^!(w?)gmnote(?:-(.*))?$/i; on('chat:message',(msg) => { if('api' === msg.type && cmdRegex.test(msg.content) && playerIsGM(msg.playerid) ){ let match=msg.content.match(cmdRegex), output = match[1].length ? '/w gm ' : '', regex; if(match[2]){ regex = new RegExp(`^${match[2]}`,'i'); } _.chain(msg.selected) .map( s => getObj('graphic',s._id)) .reject(_.isUndefined) .reject((o)=>o.get('gmnotes').length===0) .each( o => { if(regex){ let lines = _.filter( getLines(unescape(o.get('gmnotes'))), (l) => regex.test(l.replace(/<[^>]*>/g,'')) ).join('<br>'); sendChat(o.get('name'), `${output}${lines}`); } else { sendChat(o.get('name'), `${output}${unescape(o.get('gmnotes')).replace(/(?:[\n\r]+|<br\/?>)/g,'<br>')}`); } }); } }); }); I have no idea if this is the best/correct one. I use this, and the call '!wgmnote' to whisper the details to the GM. What would be amazing is if I could have another 'call' which the 'shouts' it to the players as a /desc. This way I should be able to make something like this: This can mean I can whisper the note to myself, check that it's all correct and relevant for the situation and then click the button below to send it out. - I'm currently just copying, typing in /desc and then and pasting it in again below. I tried making another api using the code from Sean above  with the call '!pcdesc' but it wasn't working for me. - I'm not sure if that's because I have multiple API's conflicting with one another, or Seans code isn't 100% correct, or... I'm just being a fool. Any help, as always would be amazing!
Just a follow up regarding my message/question above. Is it actually possible to have an API 'display' text as a /desc, directly as if the DM had typed it out themselves? I'm trying to solve my question above but aware that I may wasting my time...
1557846589
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
It should be possible, though I don't know if it would display in chat as from the GM or from the API, but if you replace '/w gm ' above with '/desc ' it should output a description. Since I don't know javascript, I'd have to make two scripts to be able to make the choice. additionally replacing: const cmdRegex = /^!(w?)gmnote(?:-(.*))?$/i; with const cmdRegex = /^!(w?)gmdesc(?:-(.*))?$/i; although I'm sure a lot of the regex could be simplified, since you can't whisper a description.
1557848368
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Hmm. just tested those changes, and didn't get the hoped for results. First is script,second is hand-typed.
Yes, I think I got a similar result when I tested a similar thing a few days ago. - It's not a huge issue, either way. - It's just to stop my players from being able to see what is a 'pre-written' bit of text and what is 'ad-hoc'.
1557869714

Edited 1557869821
The Aaron
Roll20 Production Team
API Scripter
Try this: on('ready',()=>{ const blockElements = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'pre', 'address', 'blockquote', 'dl', 'div', 'fieldset', 'form', 'hr', 'noscript', 'table','br' ]; const rStart=new RegExp(`<\\s*(?:${blockElements.join('|')})\\b[^>]*>`,'ig'); const rEnd=new RegExp(`<\\s*\\/\\s*(?:${blockElements.join('|')})\\b[^>]*>`,'ig'); const s={ desc: `font-weight:bold;font-style:italic;text-align:center;` }; const getLines = (str) => (rStart.test(str) ? str .replace(/[\n\r]+/g,' ') .replace(rStart,"\r$&") .replace(rEnd,"$&\r") .split(/[\n\r]+/) : str .split(/(?:[\n\r]+|<br\/?>)/) ) .map((s)=>s.trim()) .filter((s)=>s.length) ; const cmdRegex = /^!(w?)gmdesc(?:-(.*))?$/i; on('chat:message',(msg) => { if('api' === msg.type && cmdRegex.test(msg.content) && playerIsGM(msg.playerid) ){ let match=msg.content.match(cmdRegex), output = match[1].length ? '/w gm ' : '', regex; if(match[2]){ regex = new RegExp(`^${match[2]}`,'i'); } _.chain(msg.selected) .map( s => getObj('graphic',s._id)) .reject(_.isUndefined) .reject((o)=>o.get('gmnotes').length===0) .each( o => { if(regex){ let lines = _.filter( getLines(unescape(o.get('gmnotes'))), (l) => regex.test(l.replace(/<[^>]*>/g,'')) ).join('<br>'); sendChat(o.get('name'), `${output}<span style="${s.desc}">${lines}</span>`); } else { sendChat(o.get('name'), `${output}<span style="${s.desc}">${unescape(o.get('gmnotes')).replace(/(?:[\n\r]+|<br\/?>)/g,'<br>')}</span>`); } }); } }); }); Comparison: : I can fix that indent too, if you want.
Um, I'm sorry, I don't know if I'm being an idiot here: When I try your code, (along with the presumed call '!gmdesc') it looks like this: It has the token name showing (In my case, it's just called 'Note') I even tried it with a blank name, it then looks like this.... I'm sooo grateful for your continued amazing help! - I'm just not sure why mine ends up looking different to your example above. None of these things are super big issues #FirstWorldProblems. - So I'll understand if you'd rather leave this alone and focus on other more important things!
1557894407
The Aaron
Roll20 Production Team
API Scripter
Ah, good point. I can probably fix that, mostly. I can't get rid of the name part entirely, but I can make it blank. 
1557922068
The Aaron
Roll20 Production Team
API Scripter
Here it is with no name in the message: on('ready',()=>{ const blockElements = [ 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'pre', 'address', 'blockquote', 'dl', 'div', 'fieldset', 'form', 'hr', 'noscript', 'table','br' ]; const rStart=new RegExp(`<\\s*(?:${blockElements.join('|')})\\b[^>]*>`,'ig'); const rEnd=new RegExp(`<\\s*\\/\\s*(?:${blockElements.join('|')})\\b[^>]*>`,'ig'); const s={ desc: `font-weight:bold;font-style:italic;text-align:center;` }; const getLines = (str) => (rStart.test(str) ? str .replace(/[\n\r]+/g,' ') .replace(rStart,"\r$&") .replace(rEnd,"$&\r") .split(/[\n\r]+/) : str .split(/(?:[\n\r]+|<br\/?>)/) ) .map((s)=>s.trim()) .filter((s)=>s.length) ; const cmdRegex = /^!(w?)gmdesc(?:-(.*))?$/i; on('chat:message',(msg) => { if('api' === msg.type && cmdRegex.test(msg.content) && playerIsGM(msg.playerid) ){ let match=msg.content.match(cmdRegex), output = match[1].length ? '/w gm ' : '', regex; if(match[2]){ regex = new RegExp(`^${match[2]}`,'i'); } _.chain(msg.selected) .map( s => getObj('graphic',s._id)) .reject(_.isUndefined) .reject((o)=>o.get('gmnotes').length===0) .each( o => { if(regex){ let lines = _.filter( getLines(unescape(o.get('gmnotes'))), (l) => regex.test(l.replace(/<[^>]*>/g,'')) ).join('<br>'); sendChat('', `${output}<span style="${s.desc}">${lines}</span>`); } else { sendChat('', `${output}<span style="${s.desc}">${unescape(o.get('gmnotes')).replace(/(?:[\n\r]+|<br\/?>)/g,'<br>')}</span>`); } }); } }); });
Amazing thanks!
1557942178
The Aaron
Roll20 Production Team
API Scripter
No problem!