From f088aaf06a4b12c12d1531bdb98d41c4fc79eff9 Mon Sep 17 00:00:00 2001 From: "Aaron C. Meadows" Date: Mon, 6 May 2024 09:44:25 -0500 Subject: [PATCH] Adding in the missing Dealer 2.0 script. --- Dealer/2.0/Dealer.js | 287 +++++++++++++++++++++++++++++++++++++++++++ Dealer/Dealer.js | 151 ++++++++++++++++++++--- 2 files changed, 424 insertions(+), 14 deletions(-) create mode 100644 Dealer/2.0/Dealer.js diff --git a/Dealer/2.0/Dealer.js b/Dealer/2.0/Dealer.js new file mode 100644 index 0000000000..271ff8bbc1 --- /dev/null +++ b/Dealer/2.0/Dealer.js @@ -0,0 +1,287 @@ +// Dealer +// Last Updated: 2019-09-03 +// A script to deal and take cards to selected users from specified decks. +// Syntax is !deal --[give,take] [number of cards as integer] --[deck name]|[card name] +on('ready', () => { + const version = '2.0'; + + const processInlinerolls = (msg) => { + if(_.has(msg,'inlinerolls')){ + return _.chain(msg.inlinerolls) + .reduce(function(m,v,k){ + let 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); + },msg.content) + .value(); + } else { + return msg.content; + } + }; + + + log('-=> Dealer v' + version + ' <=-'); + + //Interface elements + + const openReport = `
`; + const openSection = `
` + const openHeader = `
`; + const openSubhead = `
`; + const openSubheadMargin = `
`; + const openPageHead = `
`; + const closeReport = `
`; + const dealerHelp = openReport + openHeader +"Dealer v."+ version + "
" + +"Dealer deals and takes cards from players sepcified by deck or deck & card. It works on selected tokens or by specifying a player_id.
" + +openSection +openSubhead + "Syntax

" + +"!deal --[give,take] [#] --[deck name]|[Card Name] --[ids|Player_id]
" + +"If 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.
" + +"You can specify a number of cards to give or take. After the action word, type an integer, after a space:
" + +"!deal --give 5 --Playing Cards
" + +"You 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:
" + +"!deal --give --Playing Cards|Six of Hearts
" + +"The 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.
" + +"If 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.
" + +"Script will try to let you know if you have not prepared a command or deck properly.
" + +"If 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.
" + +"If you do not wish to select a token, you may specify the plyer by using their player id. Use the optional
--ids|[player_id] at the end of a command. You can get a list of player ids in the campaign by using the command
!deal --players
" + +openSection +openSubhead +"Uses:

" + +"Games which deal or take playing cards at random from a standard deck
" + +"Awarding inspiration--or Bardic Inspiration from a separate deck.
" + +"Awarding a random potion/scroll/treasure/piece of equipment from a properly prepared deck.
" + +openSection +openSubhead +"Examples
" + +"!deal --give --Inspiration
" + +"This will deal a card from a deck called 'Inspiration' to the player whose token is selected. This would be ideal as a token ability or macro bar macro to allow GMs to award Inspiration to the player of a selected token.
" + +"
" + +"!deal --take --Inspiration
" + +"This will take a card from a deck called 'Inspiration' from the hand player whose token is selected. This would be ideal as a token ability to allow players to spend Inspiration
" + +"
" + +"!deal --give or
!deal --give --Playing Cards
" + +"This will deal a card from the Playing Cards deck to the player whose token is selected.
" + +"!deal --take
" + +"This will take a card from the Playing Cards deck from the hand player whose token is selected."; + + + const sendMessage = function(title,theMessage) { + sendChat ('Dealer','/w gm ' +openSection+openHeader+title+''+theMessage+closeReport); + return; + } + + on('chat:message', (msg) => { + if ('api' === msg.type && /!deal(\b\s|$)/i.test(msg.content)) { +let playerList = ''; +let IDs = ''; +const players=findObjs({_type:'player'}); + _.each(players,function (obj){ + playerList = playerList + (''+obj.get('displayname')+': '+obj.get('id'))+'
'; + }); + +if (msg.content === '!deal --players'){ + //For when I add multiple players + //let howToUse = '
To specify a player to send a card to, copy one or more player IDs above. Use them at the end of a command using the format:
--ids|ID1,ID2...
Be sure to include the dash at the beginning of an id, and to use no spaces in the string.' + let howToUse = '
To specify a player to send a card to, copy one of the player IDs above. Use it at the end of a command using the format:
--ids|ID1
Be sure to include the dash at the beginning of the id, and to use no spaces in the string. Currently, Dealer only supports one token or player ID per command.' + + //playerList = openSection+openHeader+'Player ID list'+playerList+howToUse+closeReport; + sendMessage("Player List",playerList+howToUse); + return; +}; + +if (msg.content === '!deal --help'){ + //playerList = openSection+openHeader+'Player ID list'+playerList+howToUse+closeReport; + sendChat("Dealer",dealerHelp); + return; +}; + +if (msg.content.includes(' --ids|')){ + IDs = msg.content.split(' --ids|')[1].split(','); + //For now, we only want the first. We will add the ability to do multiples later. + IDs = IDs[0]; + msg.content = msg.content.split(' --ids|')[0];} + + else{ + if(!msg.selected){ + sendMessage ("No Player Identified", "You must select a token controlled by a player, or use the --ids command to specify a player ID. Player Id's can be found with the command !deal --players."); + return; + } + + +} + + + //get parameter and use default of 'give' if parameter is missing or malformed + const args = processInlinerolls(msg).split(/\s+--/); + + if (args.length < 2) { + if (args[0] !== '!deal') { + sendMessage('Malformed Command', 'Please use !deal --[give/take] --[Deckname] --ids|player_ids.'); + return; + } else { + args[1] = 'give'; + } + } + let action = args[1].split(/\s+/)[0]; + + + let numCards = args[1].split(/\s+/)[1]; + numCards = Number((Number.isInteger(Number(numCards))) ? numCards : 1); + + const actions = ['give', 'take']; + let cardAction = 'give'; + if (action && actions.includes(action)) { + cardAction = action; + } + let choices = args[2] || 'Playing Cards'; + + + let deckChoice = choices.split(/\|/)[0] || 'Playing Cards'; + let cardChoice = choices.split(/\|/)[1] || ''; + + + + //getid of deck + let theDeck = findObjs({ + _type: "deck", + name: deckChoice + })[0]; + + //test if deck exists + if (!theDeck) { + sendMessage('No Such Deck', 'Create a deck named ' + deckChoice + '. If the intent is an Inspiration deck, it must be an infinite deck of one card only.'); + return; + } + + + let deckID = theDeck.id; + let deckCards = theDeck.get('_currentDeck'); + + + + if (msg.selected){ + + if (msg.selected.length > 1) { + sendMessage('Multiple Tokens', 'Please select only one token. It must be controlled by a specific player, or represent a player-controlled character.'); + return; + } + } + + if (msg.selected){ + let token = getObj(msg.selected[0]._type, msg.selected[0]._id); + + //assign associated character to a variable + if (!token.get('represents')) { + sendMessage('No Player Specified', 'This token does not represent a player character. Only players get cards.'); + return; + } + let character = getObj("character", token.get('represents')); + + //Get owner IDs of each -- Not needed at this point + // If the token represents a character, get the character's controller, otherwise the token's + let ownerids = (token.get('controlledby').split(',')); + + + if (character) { + ownerids = (character.get('controlledby').split(',')); + } + //reduces to one ownerid that is not ['all'] + ownerid = ownerids.filter(s => s !== 'all')[0]; + } + + if (IDs !==''){ + ownerid = IDs; + } + if (undefined===ownerid || ownerid===''){ + sendMessage ('No Player ID specified', 'Dealer does not know whom to send this card to.'); + return; + } + + // give card to player + // If the ownerid is undefined (no valid controller) explain and exit + if (!ownerid) { + sendMessage('Needs a Player Controller', 'If a token represents a character controlled by \'All Players\', an individual player must be also be specified. If there are multiple controllers, only the first will get inspiration.'); + return; + } + + //If a card is specified by name + if (cardChoice !== '') { + let theCard = findObjs({ + _type: 'card', + name: cardChoice, + _deckid: deckID + })[0]; + if (theCard !== undefined) { + do { + let specificCardID = theCard.id; + if (cardAction == 'give') { + giveCardToPlayer(specificCardID, ownerid); + numCards--; + } else { + takeCardFromPlayer(ownerid, {cardid: specificCardID}) + numCards--; + } + } + while (numCards > 0); + return; + } else { + sendMessage('No Such Card', 'There does not seem to be a card named ' + cardChoice + ' in the deck ' + deckChoice); + return; + } + } + + + //If this is a random card + do { + + //get id of card + let cardid = drawCard(deckID); + + if (!cardid) { + shuffleDeck(deckID); + cardid = drawCard(deckID); + } + // get playerId of Token controller + //assign selected token to a variable + + switch (cardAction) { + case 'take': + + let hand = findObjs({ + type: 'hand', + parentid: ownerid + })[0]; + let theHand = hand.get('currentHand'); + + cardid = (theHand.split(',').filter(x => deckCards.split(',').includes(x)))[0]; + + if (theHand.length !== 0 && cardid !== undefined) { + + takeCardFromPlayer(ownerid, { + cardid: cardid + }); + } else { + let deckName = theDeck.get('name'); + sendMessage('Deck Empty', token.get('name') + ' has no cards left to take from the ' + deckName + ' deck.'); + } + + break; + default: + giveCardToPlayer(cardid, ownerid); + break; + } + + numCards--; + } + while (numCards > 0); + } + }); +}); diff --git a/Dealer/Dealer.js b/Dealer/Dealer.js index 7cfdc78822..271ff8bbc1 100644 --- a/Dealer/Dealer.js +++ b/Dealer/Dealer.js @@ -3,19 +3,128 @@ // A script to deal and take cards to selected users from specified decks. // Syntax is !deal --[give,take] [number of cards as integer] --[deck name]|[card name] on('ready', () => { - const version = '1.2.0'; + const version = '2.0'; + + const processInlinerolls = (msg) => { + if(_.has(msg,'inlinerolls')){ + return _.chain(msg.inlinerolls) + .reduce(function(m,v,k){ + let 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); + },msg.content) + .value(); + } else { + return msg.content; + } + }; + + log('-=> Dealer v' + version + ' <=-'); + + //Interface elements + + const openReport = `
`; + const openSection = `
` + const openHeader = `
`; + const openSubhead = `
`; + const openSubheadMargin = `
`; + const openPageHead = `
`; + const closeReport = `
`; + const dealerHelp = openReport + openHeader +"Dealer v."+ version + "
" + +"Dealer deals and takes cards from players sepcified by deck or deck & card. It works on selected tokens or by specifying a player_id.
" + +openSection +openSubhead + "Syntax

" + +"!deal --[give,take] [#] --[deck name]|[Card Name] --[ids|Player_id]
" + +"If 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.
" + +"You can specify a number of cards to give or take. After the action word, type an integer, after a space:
" + +"!deal --give 5 --Playing Cards
" + +"You 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:
" + +"!deal --give --Playing Cards|Six of Hearts
" + +"The 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.
" + +"If 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.
" + +"Script will try to let you know if you have not prepared a command or deck properly.
" + +"If 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.
" + +"If you do not wish to select a token, you may specify the plyer by using their player id. Use the optional
--ids|[player_id] at the end of a command. You can get a list of player ids in the campaign by using the command
!deal --players
" + +openSection +openSubhead +"Uses:

" + +"Games which deal or take playing cards at random from a standard deck
" + +"Awarding inspiration--or Bardic Inspiration from a separate deck.
" + +"Awarding a random potion/scroll/treasure/piece of equipment from a properly prepared deck.
" + +openSection +openSubhead +"Examples
" + +"!deal --give --Inspiration
" + +"This will deal a card from a deck called 'Inspiration' to the player whose token is selected. This would be ideal as a token ability or macro bar macro to allow GMs to award Inspiration to the player of a selected token.
" + +"
" + +"!deal --take --Inspiration
" + +"This will take a card from a deck called 'Inspiration' from the hand player whose token is selected. This would be ideal as a token ability to allow players to spend Inspiration
" + +"
" + +"!deal --give or
!deal --give --Playing Cards
" + +"This will deal a card from the Playing Cards deck to the player whose token is selected.
" + +"!deal --take
" + +"This will take a card from the Playing Cards deck from the hand player whose token is selected."; + + + const sendMessage = function(title,theMessage) { + sendChat ('Dealer','/w gm ' +openSection+openHeader+title+''+theMessage+closeReport); + return; + } on('chat:message', (msg) => { - if ('api' === msg.type && /!deal(\b\s|$)/i.test(msg.content) && msg.selected) { + if ('api' === msg.type && /!deal(\b\s|$)/i.test(msg.content)) { +let playerList = ''; +let IDs = ''; +const players=findObjs({_type:'player'}); + _.each(players,function (obj){ + playerList = playerList + (''+obj.get('displayname')+': '+obj.get('id'))+'
'; + }); + +if (msg.content === '!deal --players'){ + //For when I add multiple players + //let howToUse = '
To specify a player to send a card to, copy one or more player IDs above. Use them at the end of a command using the format:
--ids|ID1,ID2...
Be sure to include the dash at the beginning of an id, and to use no spaces in the string.' + let howToUse = '
To specify a player to send a card to, copy one of the player IDs above. Use it at the end of a command using the format:
--ids|ID1
Be sure to include the dash at the beginning of the id, and to use no spaces in the string. Currently, Dealer only supports one token or player ID per command.' + + //playerList = openSection+openHeader+'Player ID list'+playerList+howToUse+closeReport; + sendMessage("Player List",playerList+howToUse); + return; +}; + +if (msg.content === '!deal --help'){ + //playerList = openSection+openHeader+'Player ID list'+playerList+howToUse+closeReport; + sendChat("Dealer",dealerHelp); + return; +}; + +if (msg.content.includes(' --ids|')){ + IDs = msg.content.split(' --ids|')[1].split(','); + //For now, we only want the first. We will add the ability to do multiples later. + IDs = IDs[0]; + msg.content = msg.content.split(' --ids|')[0];} + + else{ + if(!msg.selected){ + sendMessage ("No Player Identified", "You must select a token controlled by a player, or use the --ids command to specify a player ID. Player Id's can be found with the command !deal --players."); + return; + } + + +} //get parameter and use default of 'give' if parameter is missing or malformed - const args = msg.content.split(/\s+--/); + const args = processInlinerolls(msg).split(/\s+--/); if (args.length < 2) { if (args[0] !== '!deal') { - sendChat('Deal', '/w gm Malformed command. Please use !deal --[give/take] --[Deckname].'); + sendMessage('Malformed Command', 'Please use !deal --[give/take] --[Deckname] --ids|player_ids.'); return; } else { args[1] = 'give'; @@ -48,7 +157,7 @@ on('ready', () => { //test if deck exists if (!theDeck) { - sendChat('Deal', '/w gm Create a deck named ' + deckChoice + '. If the intent is an Inspiration deck, it must be an infinite deck of one card only.'); + sendMessage('No Such Deck', 'Create a deck named ' + deckChoice + '. If the intent is an Inspiration deck, it must be an infinite deck of one card only.'); return; } @@ -58,16 +167,20 @@ on('ready', () => { - if (msg.selected.length > 1) { - sendChat('Deal', '/w gm Please select only one token. It must represent player-controlled character.'); + if (msg.selected){ + + if (msg.selected.length > 1) { + sendMessage('Multiple Tokens', 'Please select only one token. It must be controlled by a specific player, or represent a player-controlled character.'); return; + } } + if (msg.selected){ let token = getObj(msg.selected[0]._type, msg.selected[0]._id); //assign associated character to a variable if (!token.get('represents')) { - sendChat('Deal', '/w gm This token does not represent a player character. Only players get cards.'); + sendMessage('No Player Specified', 'This token does not represent a player character. Only players get cards.'); return; } let character = getObj("character", token.get('represents')); @@ -82,12 +195,20 @@ on('ready', () => { } //reduces to one ownerid that is not ['all'] ownerid = ownerids.filter(s => s !== 'all')[0]; + } + if (IDs !==''){ + ownerid = IDs; + } + if (undefined===ownerid || ownerid===''){ + sendMessage ('No Player ID specified', 'Dealer does not know whom to send this card to.'); + return; + } // give card to player // If the ownerid is undefined (no valid controller) explain and exit if (!ownerid) { - sendChat('deal', '/w gm If a token represents a character controlled by \'All Players\', an individual player must be also be specified. If there are multiple controllers, only the first will get inspiration.'); + sendMessage('Needs a Player Controller', 'If a token represents a character controlled by \'All Players\', an individual player must be also be specified. If there are multiple controllers, only the first will get inspiration.'); return; } @@ -98,24 +219,26 @@ on('ready', () => { name: cardChoice, _deckid: deckID })[0]; - if (theCard !== undefined) { do { let specificCardID = theCard.id; - + if (cardAction == 'give') { giveCardToPlayer(specificCardID, ownerid); numCards--; + } else { + takeCardFromPlayer(ownerid, {cardid: specificCardID}) + numCards--; + } } while (numCards > 0); return; } else { - sendChat('deal', '/w gm There does not seem to be a card named ' + cardChoice + ' in the deck ' + deckChoice); + sendMessage('No Such Card', 'There does not seem to be a card named ' + cardChoice + ' in the deck ' + deckChoice); return; } } - //If this is a random card do { @@ -147,7 +270,7 @@ on('ready', () => { }); } else { let deckName = theDeck.get('name'); - sendChat('deal', '/w gm ' + token.get('name') + ' has no cards left to take from the ' + deckName + ' deck.'); + sendMessage('Deck Empty', token.get('name') + ' has no cards left to take from the ' + deckName + ' deck.'); } break;