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

Help with Decks

Hi, I have seen a few old posts of people asking this from years ago, but each of those posts ended with not yet currently available. I need help with decks. I am creating card games and I want to improve the functionality of my current decks to include the following: 1) Look at the top X cards and replace them on top of the deck in any order 2) Place X card at Y position within the deck (without seeing the other cards in the deck). Is this possible right now? Would anyone have any idea of how I might be able to get it to work. Thanks
1519356512
The Aaron
Pro
API Scripter
It can kind of be accomplished with the API.  I have a script that will let you do some parts of that (view the order of cards in the deck, swap them around).  You could write a script that would let you do both of those. One caveat is that it doesn't actually work by moving cards (the API can't affect the order of cards), it works by rewriting the card contents to swap their values.  Because of that, it can only work with cards where the images are in a User's Library (which yours probably are).
That sounds ideal, which API is that? I'll definitely give it a try.
1519365299
The Aaron
Pro
API Scripter
I think it's this one...  Looks like you run !deck to get help. (function(){     "use strict";     const numberStyle=`position:absolute;top:3px;right:3px;z-index:100;font-weight:bold;color:white;font-size:2em;text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;`,           cardStyle=`max-width: 5em;max-height:7em;position:relative;display:inline-block;`, //          nameStyle=`position:absolute;bottom:3px;left:3px;z-index:100;font-weight:bold;color:white;font-size:1em;text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;`,                    esRE = function (s) {           var escapeForRegexp = /(\\|\/|\[|\]|\(|\)|\{|\}|\?|\+|\*|\||\.|\^|\$)/g;           return s.replace(escapeForRegexp,"\\$1");         },         HE = (function(){           var entities={                   //' ' : '&'+'nbsp'+';',                   '<' : '&'+'lt'+';',                   '>' : '&'+'gt'+';',                   "'" : '&'+'#39'+';',                   '@' : '&'+'#64'+';',                   '{' : '&'+'#123'+';',                   '|' : '&'+'#124'+';',                   '}' : '&'+'#125'+';',                   '[' : '&'+'#91'+';',                   ']' : '&'+'#93'+';',                   '"' : '&'+'quot'+';'               },               re=new RegExp('('+_.map(_.keys(entities),esRE).join('|')+')','g');           return function(s){             return s.replace(re, function(c){ return entities[c] || c; });           };         }()),         ch = function (c) {             var entities = {                 '<' : 'lt',                 '>' : 'gt',                 "'" : '#39',                 '@' : '#64',                 '{' : '#123',                 '|' : '#124',                 '}' : '#125',                 '[' : '#91',                 ']' : '#93',                 '"' : 'quot',                 '-' : 'mdash',                 ' ' : 'nbsp'             };             if(_.has(entities,c) ){                 return ('&'+entities[c]+';');             }             return '';         },                  keyFormat = (s)=>s.toLowerCase().replace(/[^a-z0-9]/g,''),         showHelp = function(who){             sendChat('',`/w "${who}" `+             `<div>`+                 `<div>`+                     `<code>!deck `+                         `--${ch('<')}Deck name${ch('>')} ${ch('[')}--${ch('<')}Operation${ch('>')} ${ch('[')}--${ch('<')}Option${ch('>')} ...${ch(']')} ${ch(']')}`+                     `</code>`+                 `</div>`+                 `<div>`+                 `<h4>Operations</h4>`+                 `<ul>`+                     `<li><code>list</code> -- The default operation, shows the current stack of cards for the deck.</li>`+                     `<li><code>stack</code> -- Stack the deck with cards.  Each subsequent option is the name of a card in the deck and will be placed on the top of the deck in the order specified.  Cards that are replaced will be moved to where each new card was.</li>`+                 `</ul>`+                 `<h4>Names</h4>`+                 `<p>Wherever you specify a card or deck name, you can use the minimal matching part of the name.  Case is ignored, as are spaces and any punctuation.  ${ch('"')}Bob - the - builder!${ch('"')} would be matched by ${ch('"')}bob${ch('"')} or ${ch('"')}thebui${ch('"')} or a similar substring.</p>`+             `</div>`             );         },                  getDecks = function(name){             let deckKey=keyFormat(name||'');             return deckKey ?                 _.filter(findObjs({ type: 'deck' }), (d)=> -1 !== keyFormat(d.get('name')).indexOf(deckKey)) :                 [];         },         getCards = function(deck, cardNames){             let cards = findObjs({                 type: 'card',                 deckid: deck.id             });             return _.chain(cardNames)                 .map(keyFormat)                 .reduce((m,cardKey)=>{                     let match = _.reduce(cards,(m,c) => {                         let cKey = keyFormat(c.get('name'));                         if(-1 !== cKey.indexOf(cardKey)){                             if(cKey===cardKey){                                 return {                                     card: c,                                     exact: true                                 };                             } else if(!m.card){                                 return {                                     card: c,                                     exact: false                                 };                             }                         }                         return m;                     },{card:false, exact:false});                     m.push(match.card);                     return m;                 },[])                 .value();         },         showDeckOptions = function(who,msg,decks,deckName){             let notFound = (decks.length ? '' : `<div style="color:red;font-weight:bold;">No deck found for <code>${deckName}</code></div>`),                 deckOpt = (decks.length ? decks : findObjs({type:'deck'}));             sendChat('',`/w "${who}" `+                 `<div>`+                     notFound+                     `<div><div>Possible options:</div><ul>`+                         _.map(deckOpt,(d)=>{                             return `<li><a href="${HE(msg.replace(`--${deckName}`,`--${keyFormat(d.get('name'))}`))}">${d.get('name')}</a></li>`;                         }).join('')+                     `</ul></div>`+                 `</div>`             );                      },         showCardOptions = function(who,deck, cardNames){             sendChat('',`/w "${who}" `+                 `<div>`+                     `<div style="color:red;font-weight:bold;">No card found in <code>${deck.get('name')}</code> for <code>${cardNames.join('</code>, <code>')}</code></div>`+                     `<div>Possible options:</div>`+                     `<div>${getDeckCardList(deck)}</div>`+                 `</div>`             );         },         getDeckCardList = function(deck){             let cards=_.chain(deck.get('currentDeck').split(/\s*,\s*/))                 .map((id)=>getObj('card',id))                 .reject(_.isUndefined)                 .value();             let disp = _.map(cards,(o,idx)=>{                 return `<div style="${cardStyle}">`+                     `<span `+                         `style="${numberStyle}min-width:.1em;" `+                         `class="tipsy showtip" `+                         `title="${HE(HE(o.get('name')))}">`+                             (idx+1)+                         `</span>`+                     `<img src="${o.get('avatar')}">`+                 `</div>`;             }).join('');             return disp;         },         moveCardToPosition = function(deck,cardData,idx){             let card = findObjs({                 type: 'card',                 deckid: deck.id,                 name: cardData.name,                 avatar: cardData.avatar             })[0];             if(card){                 let cardPos=deck.get('currentDeck').split(/\s*,\s*/),                     currentIdx=cardPos[idx];                 if(currentIdx){                     let cardCur=getObj('card',currentIdx);                     card.set({                         name: cardCur.get('name'),                         avatar: cardCur.get('avatar')                     });                     cardCur.set(cardData);                 }             }         }         ;     on('ready',function(){                  on('chat:message',function(msg){             if(msg.type !== 'api' || !playerIsGM(msg.playerid)){                 return;             }             if(msg.content.match(/^!deck/)){                 let args = msg.content                         .replace(/<br\/>\n/g, ' ')                         .replace(/(\{\{(.*?)\}\})/g," $2 ")                         .trim()                         .split(/\s+--/),                     who=(getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'),                     operation = (args[2] || 'list').toLowerCase(),                     decks = getDecks(args[1]);                 if(1 === args.length){                     showHelp(who);                     return;                 }                 if(1 !== decks.length){                     showDeckOptions(who,msg.content,decks,args[1]);                     return;                 }                 switch(operation){                     case 'list':                         // show                         _.each(decks, (d)=>{                             sendChat('',`/w "${who}" `+                                 `<div><h3>Current ordering of cards</h3>`+                                 `<div>${getDeckCardList(d)}</div>`+                                 `</div>`                                 );                         });                         break;                     case 'stack': {                             let deck = _.first(decks),                                 cardNames = _.rest(args, 3),                                 stack=getCards(deck, cardNames),                                 unmatched=[];                             _.each(stack,(c,i)=>{                                 if(c===false){                                     unmatched.push(cardNames[i]);                                 }                             });                             if(unmatched.length){                                 showCardOptions(who,deck, unmatched);                                 return;                             }                             let stackData=_.map(stack,(o)=>({name: o.get('name'),avatar: o.get('avatar')}));                             _.each(stackData,(o,idx)=>{                                 moveCardToPosition(deck,o,idx);                             });                                                          sendChat('',`/w "${who}" `+                                 `<div><h3>New ordering of cards</h3>`+                                 `<div>${getDeckCardList(deck)}</div>`+                                 `</div>`                                 );                         }                         break;                                          }             }         });     }); })();
Thanks for that, I'm not very good at scripting myself. How would I convert this to create a command that would just show the first X number of cards instead of all of them?
1519388657
The Aaron
Pro
API Scripter
Adding .first(5) would show only the first 5:     getDeckCardList = function(deck){             let cards=_.chain(deck.get('currentDeck').split(/\s*,\s*/))                 .map((id)=>getObj('card',id))                 .reject(_.isUndefined) .first(5)                 .value();
Wonderful, thank you. Is it possible to get this to ignore the cards currently in the discard, on the table or in a persons hand? Currently the first 5 always shows the first 5 in the deck regardless of where on the table they are.
1519392129
The Aaron
Pro
API Scripter
Hmm... try adding .rest(deck.get('currentIndex')) in there:     getDeckCardList = function(deck){             let cards=_.chain(deck.get('currentDeck').split(/\s*,\s*/))                 .map((id)=>getObj('card',id))                 .reject(_.isUndefined) .rest(deck.get('currentIndex')) .first(5)                 .value();
Works better, thanks for being patient with this. It does what i described except: 1) if you have not dealt out any cards at all then it only shows 1 card, which isn't the top card. 2) if you have dealt out cards, or there are cards in discard or on the table then the first card in the 5 shown is always the last card pulled.
1519393390
The Aaron
Pro
API Scripter
hmm...  try .rest(deck.get('currentIndex') +1 )
That did it perfectly, thanks Now i will be working on the command to re-order thanks again. I will post with more feedback/questions when i have some
1519394111
The Aaron
Pro
API Scripter
Cool. =D
One last question Do you know of any way this could be adjusted to place a specific card into a specific location of the deck and not just the first card in the deck?
1519400378
The Aaron
Pro
API Scripter
The moveCardToPosition() function swaps cards based on an index.  To "insert" a card at that point, you'd basically need to move every card from the card you want to insert down one position until you get to the position you want that card in.  For example: A C D E B F so, to get B between A and C, you'd swap B and E: A C D B E F then swap B and D: A C B D E F then swap B and C: A B C D E F This is all because the API can't actually move cards or change the order, it can just redefine a card.