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 .
×
May your rolls be merry + bright! 🎄
Create a free account

[Script] RaiseMyHand

1590672859

Edited 1590673363
One of my players suggested that "it would be nice, in roll20, to have some way to track that player A would like to say/do something. The digital analogue (pun intended) of raising a hand / flag / signal." We liked the idea, and I threw together the following script. Edit: The leading comment explains the interface. When a player's hand is raised, her avatar color changes from its current value to neon green (or sometimes hunter orange). It's easy to spot people with raised hands by scanning avatars, and enthusiastic people can ping the map. Enjoy! /* `!raise` to raise or lower your hand, and `!lower` to lower all hands (GM only). */ (() =&gt; { 'use strict'; const LastUpdate = 1587557585; const ScriptName = '!raise'; const ScriptNamespace = 'RaiseMyHand'; const ScriptRE = /^\s*!(?:raise|lower)\s*$/; const Raise = '!raise'; const debug = false; const Abort = function(msg){ this.message = msg; }; const abort = msg =&gt; {throw new Abort(msg);}; /* Map player IDs to original avatar colors. */ const State = (() =&gt; { // View an object as an imperative finite map from // strings to values. const wrapTbl = T =&gt; { const add = (k, v) =&gt; {T[k] = v;}; const drop = k =&gt; {delete T[k];}; const get = (k, fail) =&gt; { const v = T[k]; return _.isUndefined(v) ? fail() : v; }; const lookup = k =&gt; {return get(k, () =&gt; {return null;});}; // Arguments to the apper are `f(k, v)`. const app = f =&gt; {_.each(T, (v, k) =&gt; f(k, v));}; return {add, drop, lookup, app}; }; const S = ( state[ScriptNamespace] || {version: 1, hands: {}} ); state[ScriptNamespace] = S; return { hand: wrapTbl(S.hands), pp: () =&gt; JSON.stringify(S) }; })(); const HandColor = '#39FF14'; // neon green const AltColor = '#FF7900'; // safety orange const getPlayer = playerid =&gt; { return getObj(`player`, playerid); }; const getPlayerColor = player =&gt; { return player.get('color').toUpperCase(); }; const setPlayerColor = (player, color) =&gt; { return player.set('color', color); }; const raiseHand = playerid =&gt; { if(debug) log(`raiseHand`); const mcolor = State.hand.lookup(playerid); const changed = mcolor === null; if(changed){ const player = getPlayer(playerid); const oldColor = getPlayerColor(player); const newColor = (oldColor === HandColor) ? AltColor : HandColor; State.hand.add(playerid, oldColor); setPlayerColor(player, newColor); if(debug) log(` raised hand: player ${playerid}, oldColor ${oldColor}, newColor ${newColor}`); } return changed; }; const lowerHand = playerid =&gt; { if(debug) log(`lowerHand`); const mcolor = State.hand.lookup(playerid); const changed = mcolor !== null; if(changed){ const player = getPlayer(playerid); State.hand.drop(playerid); setPlayerColor(player, mcolor); if(debug) log(` lowered hand: player ${playerid}, mcolor ${mcolor}`); } return changed; }; const raiseOrLowerHand = playerid =&gt; { return raiseHand(playerid) || lowerHand(playerid); }; const lowerAllHands = () =&gt; { if(debug) log(`lowerAllHands`); const apper = (playerid, color) =&gt; { lowerHand(playerid); }; State.hand.app(apper); }; const exnMessage = e =&gt; { if(e instanceof Abort) return e.message; else{ return `&lt;div&gt;Unexpected exception&lt;/div&gt;`+ '&lt;div style="font-size: .6em; line-height: 1em; margin:.1em .1em .1em 1em; padding: .1em .3em; color: #666666; border: 1px solid #999999; border-radius: .2em; background-color: white;"&gt;'+ JSON.stringify(e.stack)+ '&lt;/div&gt;'; } }; // NB side-effects on `msg` can confuse other scripts: every // registered chat handler sees the same `msg` object // ([see the forum](<a href="https://app.roll20.net/forum/permalink/1404015/" rel="nofollow">https://app.roll20.net/forum/permalink/1404015/</a>)). const onChatMessage = msg =&gt; { if('api' !== msg.type || !msg.content.match(ScriptRE)) return; const playerid = msg.playerid; try{ if(msg.content.indexOf(Raise) !== -1) raiseOrLowerHand(playerid); else if(playerIsGM(playerid)) lowerAllHands(); else abort('Only a GM can do that.'); } catch(e){ // What's wrong with just using `msg.who`? const player = getObj('player', playerid); const who = player ? player.get('_displayname') : msg.who; const error = exnMessage(e); sendChat(ScriptName, `/w "${who}" &lt;div style="border: 1px solid black; background-color: #ffeeee; padding: .2em; border-radius: .4em;" &gt;${error}&lt;/div&gt;`); } }; on('ready', () =&gt; { if(debug) log(`state.${ScriptNamespace} = ${State.pp()}`); on('chat:message', onChatMessage); log(`-=&gt; ${ScriptName} &lt;=- [${new Date(LastUpdate*1000)}]`); }); })();
I thought there was one on the one-click already? is it different than the one found here? <a href="https://app.roll20.net/forum/post/7580899/script-hands-up" rel="nofollow">https://app.roll20.net/forum/post/7580899/script-hands-up</a>
1590676301
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
I think you may have reinvented the wheel. :( This sounds nearly identical to Stephen L's "Hands Up" script in the One Click install.
1590676548
The Aaron
Roll20 Production Team
API Scripter
Still, neat to see they way you wrote it, I feel like I can learn some things from it. =D
1590678106
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
That's high praise!
Thanks for the feedback! I hadn't seen Stephen L's "Hands Up". From a quick scan of Stephen's code, the chief differences I see (beyond packaging) appear to be that Hands Up is a bit chattier (!raise is silent) and it cycles through colors (!raise just sets an obnoxious color). (High praise indeed! I learned a lot by reading scripts you wrote, The Aaron, and I rather enjoyed the experience.)
Cool, this is good to know. Dave said: Thanks for the feedback! I hadn't seen Stephen L's "Hands Up". From a quick scan of Stephen's code, the chief differences I see (beyond packaging) appear to be that Hands Up is a bit chattier (!raise is silent) and it cycles through colors (!raise just sets an obnoxious color). (High praise indeed! I learned a lot by reading scripts you wrote, The Aaron, and I rather enjoyed the experience.)