├── config ├── cards │ ├── BGG_a.json │ ├── .gitignore │ ├── Official3rdExpansionAmerica_a.json │ ├── Official3rdExpansionAmerica_q.json │ ├── Official2ndExpansionAmerica_q.json │ ├── Official2ndExpansionAmerica_a.json │ ├── BGGAmerica_q.json │ ├── OfficialChristmasExpansion_q.json │ ├── OfficialBaseSetAmerica_q.json │ ├── OfficialCanadianExpansion_q.json │ ├── Official2ndExpansion_q.json │ ├── OfficialChristmasExpansion_a.json │ ├── OfficialCanadianExpansion_a.json │ ├── OfficialBaseSetAmerica_a.json │ ├── Official3rdExpansion_q.json │ ├── Official2ndExpansion_a.json │ ├── Official3rdExpansion_a.json │ ├── OfficialBaseSet_q.json │ └── BGG_q.json ├── env │ ├── all.js │ ├── development.json │ └── production.json ├── commands.js └── config.js ├── .gitignore ├── .nodemonignore ├── CHANGELOG.md ├── app ├── models │ ├── card.js │ └── player.js ├── controllers │ ├── cards.js │ ├── games.js │ └── game.js └── bot.js ├── app.js ├── package.json ├── gruntfile.js └── README.md /config/cards/BGG_a.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /config/cards/.gitignore: -------------------------------------------------------------------------------- 1 | *-custom.json 2 | Custom* -------------------------------------------------------------------------------- /.nodemonignore: -------------------------------------------------------------------------------- 1 | # Generated by grunt-nodemon 2 | README.md 3 | node_modules/** 4 | -------------------------------------------------------------------------------- /config/env/all.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | rootPath = path.normalize(__dirname + '/../..'); 3 | 4 | module.exports = { 5 | root: rootPath 6 | }; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #0.6.0 2 | * Added !pick command 3 | * Added point limit option to games 4 | * Default NODE_ENV changed to `production` 5 | 6 | #pre-0.6.0 7 | * Change log started in 0.6.0 :) 8 | -------------------------------------------------------------------------------- /config/cards/Official3rdExpansionAmerica_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "Crying into the pages of Sylvia Plath", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 3rd Expansion" 9 | } 10 | ] -------------------------------------------------------------------------------- /config/cards/Official3rdExpansionAmerica_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "As part of his daily regimen, Anderson Cooper sets aside 15 minutes for %s.", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 3rd Expansion" 9 | } 10 | ] -------------------------------------------------------------------------------- /app/models/card.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | var Card = function Card(card) { 4 | var self = this; 5 | self.id = _.uniqueId(); 6 | self.type = card.type || ''; 7 | self.draw = card.draw || 0; 8 | self.pick = card.pick || 0; 9 | self.value = card.value || 'A bug in the mainframe (please file a bug report, if you actually get this card)'; 10 | }; 11 | 12 | /** 13 | * Expose `Card()` 14 | */ 15 | exports = module.exports = Card; 16 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cards Against Humanity IRC bot 3 | * main application script 4 | * @author Teemu Lahti 5 | * @version 0.6.0 6 | */ 7 | console.log('Cards Against Humanity IRC bot'); 8 | 9 | // Set node env 10 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'; 11 | 12 | // dependencies 13 | var bot = require('./app/bot'); 14 | 15 | // init the bot 16 | bot.init(); 17 | // load channel command definitions 18 | require('./config/commands.js')(bot); 19 | -------------------------------------------------------------------------------- /config/cards/Official2ndExpansionAmerica_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "Charades was ruined for me forever when my mom had to act out %s.", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 2nd Expansion" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "Tonight on 20/20: What you don't know about %s could kill you.", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official 2nd Expansion" 17 | } 18 | ] -------------------------------------------------------------------------------- /app/models/player.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'), 2 | Cards = require('../controllers/cards'); 3 | 4 | var Player = function Player(nick, user, hostname) { 5 | var self = this; 6 | self.id = _.uniqueId('card'); 7 | self.nick = nick; 8 | self.user = user; 9 | self.hostname = hostname; 10 | self.cards = new Cards(); 11 | self.hasPlayed = false; 12 | self.isCzar = false; 13 | self.points = 0; 14 | self.inactiveRounds = 0; 15 | }; 16 | 17 | /** 18 | * Expose `Player()` 19 | */ 20 | exports = module.exports = Player; 21 | -------------------------------------------------------------------------------- /config/commands.js: -------------------------------------------------------------------------------- 1 | var Games = require('../app/controllers/games.js'); 2 | 3 | module.exports = function(app) { 4 | var games = new Games(); 5 | app.cmd('start', '', games.start); 6 | app.cmd('stop', 'o', games.stop); 7 | app.cmd('join', '', games.join); 8 | app.cmd('quit', '', games.quit); 9 | app.cmd('cards', '', games.cards); 10 | app.cmd('play', '', games.play); 11 | app.cmd('list', '', games.list); 12 | app.cmd('winner', '', games.winner); 13 | app.cmd('points', '', games.points); 14 | app.cmd('status', '', games.status); 15 | app.cmd('pause', '', games.pause); 16 | app.cmd('resume', '', games.resume); 17 | app.cmd('pick', '', games.pick); 18 | }; 19 | -------------------------------------------------------------------------------- /config/env/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": "irc.saunalahti.fi", 3 | "nick": "cah-dev", 4 | "setTopic": true, 5 | "topicBase": "|| This will be appended to the topic along with the game status.", 6 | "notifyUsers": true, 7 | "pointLimit": 1, 8 | "connectCommands": [ 9 | { 10 | "target": "", 11 | "message": "" 12 | } 13 | ], 14 | "joinCommands": { 15 | "#pi-cah-dev": [ 16 | { 17 | "target": "#pi-cah-dev", 18 | "message": "Hello guys" 19 | } 20 | ] 21 | }, 22 | "clientOptions": { 23 | "userName": "cah", 24 | "debug": true, 25 | "channels": ["#pi-cah-dev"], 26 | "floodProtection": true, 27 | "floodProtectionDelay": 2000 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /config/env/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": "openirc.snt.utwente.nl", 3 | "nick": "cah", 4 | "setTopic": true, 5 | "topicBase": "|| This will be appended to the topic along with the game status.", 6 | "notifyUsers": false, 7 | "pointLimit": 10, 8 | "connectCommands": [ 9 | { 10 | "target": "", 11 | "message": "" 12 | } 13 | ], 14 | "joinCommands": { 15 | "#cah": [ 16 | { 17 | "target": "", 18 | "message": "" 19 | } 20 | ] 21 | }, 22 | "clientOptions": { 23 | "userName": "cah", 24 | "channels": ["#cah"], 25 | "floodProtection": true, 26 | "floodProtectionDelay": 1000 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /config/cards/Official2ndExpansionAmerica_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "Getting hilariously gang-banged by the Blue Man Group", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 2nd Expansion" 9 | }, 10 | { 11 | "type": "Answer", 12 | "value": "Spring break!", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official 2nd Expansion" 17 | }, 18 | { 19 | "type": "Answer", 20 | "value": "The grey nutrient broth that sustains Mitt Romney", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official 2nd Expansion" 25 | }, 26 | { 27 | "type": "Answer", 28 | "value": "The mere concept of Applebee's", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official 2nd Expansion" 33 | } 34 | ] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "irc-cah", 3 | "version": "0.6.0", 4 | "description": "Cards Against Humanity IRC bot", 5 | "main": "app.js", 6 | "author": "Teemu Lahti ", 7 | "license": "Creative Commons BY-NC-SA 2.0", 8 | "scripts": { 9 | "start": "node node_modules/grunt-cli/bin/grunt", 10 | "test": "node node_modules/grunt-cli/bin/grunt test" 11 | }, 12 | "dependencies": { 13 | "underscore": "latest", 14 | "grunt": "latest", 15 | "grunt-cli": "latest", 16 | "irc": "~0.3.6", 17 | "path": "~0.4.9", 18 | "irc-colors": "~1.0.3", 19 | "grunt-nodemon": "~0.2.1", 20 | "nodemon": "~1.0.17", 21 | "jayschema": "~0.2.7" 22 | }, 23 | "devDependencies": { 24 | "grunt-contrib-watch": "latest", 25 | "grunt-contrib-jshint": "latest", 26 | "grunt-nodemon": "latest", 27 | "grunt-concurrent": "latest", 28 | "grunt-mocha-test": "latest", 29 | "load-grunt-tasks": "~0.1.0", 30 | "time-grunt": "~0.1.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /config/cards/BGGAmerica_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "After Billy Mays died, he sold God %s.", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "BGG Suggestions" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "Chili's special tonight: %s with a side of %s.", 13 | "keep": "Yes", 14 | "draw": 1, 15 | "pick": 2, 16 | "source": "BGG Suggestions" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "Instead of endorsing Jell-O Pudding Pops, Bill Cosby now endorses %s.", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "BGG Suggestions" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "Mr. Owl, how many licks does it take to get to the center of %s?", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "BGG Suggestions" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "Sizzler's special tonight: %s with a side of %s.", 37 | "keep": "Yes", 38 | "draw": 1, 39 | "pick": 2, 40 | "source": "BGG Suggestions" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "You're listening to KB969, %s radio!", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "BGG Suggestions" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "What makes Fred Phelps hard?", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "BGG Suggestions" 57 | } 58 | ] -------------------------------------------------------------------------------- /config/cards/OfficialChristmasExpansion_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "After blacking out during New Year's Eve, I was awoken by %s.", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official Christmas Set" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "Every Christmas, my uncle gets drunk and tells the story about %s.", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Christmas Set" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "Jesus is %s.", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official Christmas Set" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "On the third day of Christmas, my true love gave to me: three French hens, two turtle doves and %s.", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official Christmas Set" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "This holiday season, Tim Allen must overcome his fear of %s to save Christmas.", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Christmas Set" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "Wake up, America. Christmas is under attack by secular liberals and their %s.", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official Christmas Set" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "What keeps me warm during the cold, cold winter?", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Christmas Set" 57 | } 58 | ] -------------------------------------------------------------------------------- /config/cards/OfficialBaseSetAmerica_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "BILLY MAYS HERE FOR %s!", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official Base Set" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "Due to a PR fiasco, Walmart no longer offers %s.", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Base Set" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "In the new Disney Channel Original Movie, Hannah Montana struggles with %s for the first time.", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official Base Set" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "Lifetime presents %s, the story of %s.", 29 | "keep": "Yes", 30 | "draw": 1, 31 | "pick": 2, 32 | "source": "CaH Official Base Set" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "Major League Baseball has banned %s for giving players an unfair advantage.", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Base Set" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "Next on ESPN2: The World Series of %s.", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official Base Set" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "TSA guidelines now prohibit %s on airplanes.", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Base Set" 57 | }, 58 | { 59 | "type": "Question", 60 | "value": "What's Teach for America using to inspire inner city students to succeed?", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official Base Set" 65 | } 66 | ] -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // show elapsed time at the end 4 | require('time-grunt')(grunt); 5 | // load all grunt tasks 6 | require('load-grunt-tasks')(grunt); 7 | 8 | // Project Configuration 9 | var yeomanConfig = { 10 | app: 'app' 11 | }; 12 | 13 | grunt.initConfig({ 14 | yeoman: yeomanConfig, 15 | pkg: grunt.file.readJSON('package.json'), 16 | watch: { 17 | js: { 18 | files: ['app.js', 'app/**/*.js'], 19 | tasks: ['jshint'], 20 | options: { 21 | livereload: true, 22 | } 23 | } 24 | }, 25 | jshint: { 26 | all: ['gruntfile.js', 'app.js', 'app/**/*.js'] 27 | }, 28 | nodemon: { 29 | dev: { 30 | options: { 31 | file: 'app.js', 32 | args: [], 33 | ignoredFiles: ['README.md', 'node_modules/**'], 34 | watchedExtensions: ['js'], 35 | watchedFolders: ['app', 'config'], 36 | debug: true, 37 | delayTime: 1, 38 | env: { 39 | }, 40 | cwd: __dirname 41 | } 42 | } 43 | }, 44 | 45 | concurrent: { 46 | tasks: ['nodemon', 'watch'], 47 | options: { 48 | logConcurrentOutput: true 49 | } 50 | } 51 | }); 52 | 53 | //Load NPM tasks 54 | grunt.loadNpmTasks('grunt-contrib-watch'); 55 | grunt.loadNpmTasks('grunt-contrib-jshint'); 56 | grunt.loadNpmTasks('grunt-nodemon'); 57 | grunt.loadNpmTasks('grunt-concurrent'); 58 | 59 | //Making grunt default to force in order not to break the project. 60 | grunt.option('force', true); 61 | 62 | //Default task(s). 63 | grunt.registerTask('default', ['jshint', 'concurrent']); 64 | 65 | //Test task. 66 | grunt.registerTask('test', []); 67 | }; -------------------------------------------------------------------------------- /config/cards/OfficialCanadianExpansion_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "After unifying the GST and PST the Government can now afford to provide %s for %s.", 5 | "keep": "Yes", 6 | "draw": 1, 7 | "pick": 2, 8 | "source": "CaH Official Canadian Expansion" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "Air Canada guidelines now prohibit %s on airplanes.", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Canadian Expansion" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "CTV presents %s, the story of %s.", 21 | "keep": "Yes", 22 | "draw": 1, 23 | "pick": 2, 24 | "source": "CaH Official Canadian Expansion" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "If %s came in two-fours Canada would be more %s?", 29 | "keep": "Yes", 30 | "draw": 1, 31 | "pick": 2, 32 | "source": "CaH Official Canadian Expansion" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "In an attempt to reach a wider audience, the Royal Ontario Museum has opened an interactive exhibit on %s.", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Canadian Expansion" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "In the next Bob and Doug McKenzie adventure, they have to find %s to uncover a sinister plot involving %s and %s.", 45 | "keep": "Yes", 46 | "draw": 2, 47 | "pick": 3, 48 | "source": "CaH Official Canadian Expansion" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "In Vancouver, %s is now legal.", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Canadian Expansion" 57 | }, 58 | { 59 | "type": "Question", 60 | "value": "O Canada, we stand on guard for %s.", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official Canadian Expansion" 65 | }, 66 | { 67 | "type": "Question", 68 | "value": "What's the Canadian government using to inspire rural students to succeed?", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official Canadian Expansion" 73 | } 74 | ] -------------------------------------------------------------------------------- /config/cards/Official2ndExpansion_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "After months of debate, the Occupy Wall Street General Assembly could only agree on \"More %s!\"", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 2nd Expansion" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "Before %s, all we had was %s.", 13 | "keep": "Yes", 14 | "draw": 1, 15 | "pick": 2, 16 | "source": "CaH Official 2nd Expansion" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "Everyone down on the ground! We don't want to hurt anyone. We're just here for %s.", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official 2nd Expansion" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "I spent my whole life working toward %s, only to have it ruined by %s.", 29 | "keep": "Yes", 30 | "draw": 1, 31 | "pick": 2, 32 | "source": "CaH Official 2nd Expansion" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "I went from %s to %s, all thanks to %s.", 37 | "keep": "Yes", 38 | "draw": 2, 39 | "pick": 3, 40 | "source": "CaH Official 2nd Expansion" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "If God didn't want us to enjoy %s, he wouldn't have given us %s.", 45 | "keep": "Yes", 46 | "draw": 1, 47 | "pick": 2, 48 | "source": "CaH Official 2nd Expansion" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "Little Miss Muffet Sat on a tuffet, Eating her curds and %s.", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official 2nd Expansion" 57 | }, 58 | { 59 | "type": "Question", 60 | "value": "Members of New York's social elite are paying thousands of dollars just to experience %s.", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official 2nd Expansion" 65 | }, 66 | { 67 | "type": "Question", 68 | "value": "My country, 'tis of thee, sweet land of %s.", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official 2nd Expansion" 73 | }, 74 | { 75 | "type": "Question", 76 | "value": "Next time on Dr. Phil: How to talk to your child about %s.", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official 2nd Expansion" 81 | }, 82 | { 83 | "type": "Question", 84 | "value": "Only two things in life are certain: death and %s.", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official 2nd Expansion" 89 | }, 90 | { 91 | "type": "Question", 92 | "value": "The healing process began when I joined a support group for victims of %s.", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official 2nd Expansion" 97 | }, 98 | { 99 | "type": "Question", 100 | "value": "The votes are in, and the new high school mascot is %s.", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official 2nd Expansion" 105 | }, 106 | { 107 | "type": "Question", 108 | "value": "This month's Cosmo: \"Spice up your sex life by bringing %s into the bedroom.\"", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "CaH Official 2nd Expansion" 113 | } 114 | ] 115 | -------------------------------------------------------------------------------- /app/controllers/cards.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'), 2 | Card = require('../models/card'); 3 | 4 | var Cards = function Cards(cards) { 5 | var self = this; 6 | 7 | self.cards = []; 8 | 9 | // add all cards in init array 10 | _.each(cards, function (c) { 11 | var card; 12 | if(c instanceof Card) { 13 | card = c; 14 | } else if(c.hasOwnProperty('value')) { 15 | card = new Card(c); 16 | } else { 17 | console.warning('Invalid card', c); 18 | } 19 | self.cards.push(card); 20 | }); 21 | 22 | /** 23 | * Reset the collection 24 | * @param cards Optional replacement list of cards 25 | * @returns {Array} Array of the old, replaced cards 26 | */ 27 | self.reset = function(cards) { 28 | if(typeof cards === 'undefined') { 29 | cards = []; 30 | } 31 | var oldCards = self.cards; 32 | self.cards = cards; 33 | return oldCards; 34 | }; 35 | 36 | /** 37 | * Shuffle the cards 38 | * @returns {Cards} The shuffled collection 39 | */ 40 | self.shuffle = function () { 41 | self.cards = _.shuffle(self.cards); 42 | return self; 43 | }; 44 | 45 | /** 46 | * Add card to collection 47 | * @param card 48 | * @returns {*} 49 | */ 50 | self.addCard = function (card) { 51 | self.cards.push(card); 52 | return card; 53 | }; 54 | 55 | /** 56 | * Remove a card from the collection 57 | * @param card 58 | * @returns {*} 59 | */ 60 | self.removeCard = function (card) { 61 | if (typeof card !== 'undefined') { 62 | self.cards = _.without(self.cards, card); 63 | } 64 | return card; 65 | }; 66 | 67 | /** 68 | * Pick cards from the collection 69 | * @param index (int|Array) Index of a single card, of Array of multiple indexes to remove and return 70 | * @returns {Card|Cards} Instance of a single card, or instance of Cards if multiple indexes picked 71 | */ 72 | self.pickCards = function (index) { 73 | if (typeof index === 'undefined') index = 0; 74 | if (index instanceof Array) { 75 | // get multiple cards 76 | var pickedCards = new Cards(); 77 | // first get all cards 78 | _.each(index, function (i) { 79 | var c = self.cards[i]; 80 | if (typeof c === 'undefined') { 81 | throw new Error('Invalid card index'); 82 | } 83 | // cards.push(); 84 | pickedCards.addCard(c); 85 | }, this); 86 | // then remove them 87 | self.cards = _.without.apply(this, _.union([self.cards], pickedCards.cards)); 88 | // _.each(pickedCards, function(card) { 89 | // self.cards.removeCard(card); 90 | // }, this); 91 | console.log('picked cards:'); 92 | console.log(_.pluck(pickedCards.cards, 'id')); 93 | console.log(_.pluck(pickedCards.cards, 'value')); 94 | console.log('remaining cards:'); 95 | console.log(_.pluck(self.cards, 'id')); 96 | console.log(_.pluck(self.cards, 'value')); 97 | return pickedCards; 98 | } else { 99 | var card = self.cards[index]; 100 | self.removeCard(card); 101 | return card; 102 | } 103 | }; 104 | 105 | /** 106 | * Get all cards in collection 107 | * @returns {Array} 108 | */ 109 | self.getCards = function() { 110 | return self.cards; 111 | }; 112 | 113 | /** 114 | * Get amount of cards in collection 115 | * @returns {Number} 116 | */ 117 | self.numCards = function () { 118 | return this.cards.length; 119 | }; 120 | }; 121 | 122 | /** 123 | * Expose `Cards()` 124 | */ 125 | exports = module.exports = Cards; 126 | -------------------------------------------------------------------------------- /app/bot.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'), 2 | irc = require('irc'), 3 | config = require('../config/config'), 4 | client, 5 | commands = [], 6 | msgs = []; 7 | 8 | function checkUserMode(message, mode) { 9 | return true; 10 | } 11 | 12 | /** 13 | * Initialize the bot 14 | */ 15 | exports.init = function () { 16 | console.log('Initializing...'); 17 | // init irc client 18 | console.log('Connecting to ' + config.server + ' as ' + config.nick + '...'); 19 | client = new irc.Client(config.server, config.nick, config.clientOptions); 20 | 21 | // handle connection to server for logging 22 | client.addListener('registered', function (message) { 23 | console.log('Connected to server ' + message.server); 24 | // Send connect commands after joining a server 25 | if (typeof config.connectCommands !== 'undefined' && config.connectCommands.length > 0) { 26 | _.each(config.connectCommands, function (cmd) { 27 | if(cmd.target && cmd.message) { 28 | client.say(cmd.target, cmd.message); 29 | } 30 | }); 31 | } 32 | }); 33 | 34 | // handle joins to channels for logging 35 | client.addListener('join', function (channel, nick, message) { 36 | console.log('Joined ' + channel + ' as ' + nick); 37 | // Send join command after joining a channel 38 | if (typeof config.joinCommands !== 'undefined' && config.joinCommands.hasOwnProperty(channel) && config.joinCommands[channel].length > 0) { 39 | _.each(config.joinCommands[channel], function (cmd) { 40 | if(cmd.target && cmd.message) { 41 | client.say(cmd.target, cmd.message); 42 | } 43 | }); 44 | } 45 | }); 46 | 47 | // output errors 48 | client.addListener('error', function (message) { 49 | console.warn('IRC client error: ', message); 50 | }); 51 | 52 | client.addListener('message', function (from, to, text, message) { 53 | console.log('message from ' + from + ' to ' + to + ': ' + text); 54 | // parse command 55 | var cmdArr = text.match(/^[\.|!](\w+)\s?(.*)$/); 56 | if (!cmdArr || cmdArr.length <= 1) { 57 | // command not found 58 | return false; 59 | } 60 | var cmd = cmdArr[1]; 61 | // parse arguments 62 | var cmdArgs = []; 63 | if (cmdArr.length > 2) { 64 | cmdArgs = _.map(cmdArr[2].match(/(\w+)\s?/gi), function (str) { 65 | return str.trim(); 66 | }); 67 | } 68 | // build callback options 69 | 70 | if (config.clientOptions.channels.indexOf(to) >= 0) { 71 | // public commands 72 | _.each(commands, function (c) { 73 | if (cmd === c.cmd) { 74 | console.log('command: ' + c.cmd); 75 | // check user mode 76 | if (checkUserMode(message, c.mode)) { 77 | c.callback(client, message, cmdArgs); 78 | } 79 | } 80 | }, this); 81 | } else if (config.nick === to) { 82 | // private message commands 83 | _.each(msgs, function (c) { 84 | if (cmd === c.cmd) { 85 | console.log('command: ' + c.cmd); 86 | // check user mode 87 | if (checkUserMode(message, c.mode)) { 88 | c.callback(client, message, cmdArgs); 89 | } 90 | } 91 | }, this); 92 | } 93 | }); 94 | }; 95 | 96 | /** 97 | * Add a public command to the bot 98 | * @param cmd Command keyword 99 | * @param mode User mode that is allowed 100 | * @param cb Callback function 101 | */ 102 | exports.cmd = function (cmd, mode, cb) { 103 | commands.push({ 104 | cmd: cmd, 105 | mode: mode, 106 | callback: cb 107 | }); 108 | }; 109 | 110 | /** 111 | * Add a msg command to the bot 112 | * @param cmd Command keyword 113 | * @param mode User mode that is allowed 114 | * @param cb Callback function 115 | */ 116 | exports.msg = function (cmd, mode, cb) { 117 | msgs.push({ 118 | cmd: cmd, 119 | mode: mode, 120 | callback: cb 121 | }); 122 | }; 123 | -------------------------------------------------------------------------------- /config/cards/OfficialChristmasExpansion_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "A Christmas stocking full of coleslaw", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official Christmas Set" 9 | }, 10 | { 11 | "type": "Answer", 12 | "value": "A Hungry-Man Frozen Christmas Dinner for One", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Christmas Set" 17 | }, 18 | { 19 | "type": "Answer", 20 | "value": "A toxic family environment", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official Christmas Set" 25 | }, 26 | { 27 | "type": "Answer", 28 | "value": "A visually arresting turtleneck", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official Christmas Set" 33 | }, 34 | { 35 | "type": "Answer", 36 | "value": "Another shitty year", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Christmas Set" 41 | }, 42 | { 43 | "type": "Answer", 44 | "value": "Clearing a bloody path through Walmart with a scimitar", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official Christmas Set" 49 | }, 50 | { 51 | "type": "Answer", 52 | "value": "Eating an entire snowman", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Christmas Set" 57 | }, 58 | { 59 | "type": "Answer", 60 | "value": "Elf cum", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official Christmas Set" 65 | }, 66 | { 67 | "type": "Answer", 68 | "value": "Fucking up \"Silent Night\" in front of 300 parents", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official Christmas Set" 73 | }, 74 | { 75 | "type": "Answer", 76 | "value": "Gift-wrapping a live hamster", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official Christmas Set" 81 | }, 82 | { 83 | "type": "Answer", 84 | "value": "Immaculate conception", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official Christmas Set" 89 | }, 90 | { 91 | "type": "Answer", 92 | "value": "Krampus, the Austrian Christmas monster", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official Christmas Set" 97 | }, 98 | { 99 | "type": "Answer", 100 | "value": "Mall Santa", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official Christmas Set" 105 | }, 106 | { 107 | "type": "Answer", 108 | "value": "My hot cousin", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "CaH Official Christmas Set" 113 | }, 114 | { 115 | "type": "Answer", 116 | "value": "Pretending to be happy", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official Christmas Set" 121 | }, 122 | { 123 | "type": "Answer", 124 | "value": "Santa's heavy sack", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official Christmas Set" 129 | }, 130 | { 131 | "type": "Answer", 132 | "value": "Several intertwining love stories featuring Hugh Grant", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official Christmas Set" 137 | }, 138 | { 139 | "type": "Answer", 140 | "value": "Socks", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "CaH Official Christmas Set" 145 | }, 146 | { 147 | "type": "Answer", 148 | "value": "Space Jam on VHS", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official Christmas Set" 153 | }, 154 | { 155 | "type": "Answer", 156 | "value": "Taking down Santa with a surface-to-air missile", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official Christmas Set" 161 | }, 162 | { 163 | "type": "Answer", 164 | "value": "The Star Wars Holiday Special", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official Christmas Set" 169 | }, 170 | { 171 | "type": "Answer", 172 | "value": "The tiny, calloused hands of the Chinese children that made the computer running this game", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official Christmas Set" 177 | }, 178 | { 179 | "type": "Answer", 180 | "value": "Whatever Kwanzaa is supposed to be about", 181 | "keep": "Yes", 182 | "draw": 0, 183 | "pick": 1, 184 | "source": "CaH Official Christmas Set" 185 | } 186 | ] -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | JaySchema = require('jayschema'), 3 | _ = require('underscore'); 4 | 5 | /** 6 | * Load and validate a card file 7 | * @param identifier Identifier of the card file 8 | * @param filename Filename of the card file 9 | */ 10 | function loadCardFile(identifier, filename) { 11 | console.log('Loading ' + identifier + ': ' + filename); 12 | if (fs.existsSync(filename)) { 13 | var data = require(filename); 14 | validator.validate(data, schema, function (errors) { 15 | if (errors) { 16 | console.error(identifier + ': Validation error'); 17 | console.error(errors); 18 | } else { 19 | console.log(identifier + ': Validation OK!'); 20 | config.cards = _.union(config.cards, data); 21 | } 22 | }); 23 | } else { 24 | console.error('File does not exists'); 25 | } 26 | } 27 | 28 | // Initialize base configuration and ENV 29 | var config = _.extend( 30 | require(__dirname + '/../config/env/all.js'), 31 | require(__dirname + '/../config/env/' + process.env.NODE_ENV + '.json') || {}, 32 | { cards: [] } 33 | ); 34 | 35 | // check custom card files and create them if they don't exist 36 | if (!fs.existsSync(__dirname + '/../config/cards/Custom_a.json')) { 37 | fs.writeFileSync(__dirname + '/../config/cards/Custom_a.json', '[]'); 38 | } 39 | if (!fs.existsSync(__dirname + '/../config/cards/Custom_q.json')) { 40 | fs.writeFileSync(__dirname + '/../config/cards/Custom_q.json', '[]'); 41 | } 42 | 43 | // All card file paths. You can comment out the ones you don't want to use. 44 | var cardFiles = { 45 | OfficialBaseSetQuestions: __dirname + '/../config/cards/OfficialBaseSet_q.json', 46 | OfficialBaseSetAmericaQuestions: __dirname + '/../config/cards/OfficialBaseSetAmerica_q.json', 47 | Official2ndExpansionQuestions: __dirname + '/../config/cards/Official2ndExpansion_q.json', 48 | Official2ndExpansionAmericaQuestions: __dirname + '/../config/cards/Official2ndExpansionAmerica_q.json', 49 | Official3rdExpansionQuestions: __dirname + '/../config/cards/Official3rdExpansion_q.json', 50 | Official3rdExpansionAmericaQuestions: __dirname + '/../config/cards/Official3rdExpansionAmerica_q.json', 51 | OfficialCanadianExpansionQuestions: __dirname + '/../config/cards/OfficialCanadianExpansion_q.json', 52 | OfficialChristmasExpansionQuestions: __dirname + '/../config/cards/OfficialChristmasExpansion_q.json', 53 | BGGQuestions: __dirname + '/../config/cards/BGG_q.json', 54 | BGGAmericaQuestions: __dirname + '/../config/cards/BGGAmerica_q.json', 55 | CustomQuestions: __dirname + '/../config/cards/Custom_q.json', 56 | OfficialBaseSetAnswers: __dirname + '/../config/cards/OfficialBaseSet_a.json', 57 | OfficialBaseSetAmericaAnswers: __dirname + '/../config/cards/OfficialBaseSetAmerica_a.json', 58 | Official2ndExpansionAnswers: __dirname + '/../config/cards/Official2ndExpansion_a.json', 59 | Official2ndExpansionAmericaAnswers: __dirname + '/../config/cards/Official2ndExpansionAmerica_a.json', 60 | Official3rdExpansionAnswers: __dirname + '/../config/cards/Official3rdExpansion_a.json', 61 | Official3rdExpansionAmericaAnswers: __dirname + '/../config/cards/Official3rdExpansionAmerica_a.json', 62 | OfficialCanadianExpansionAnswers: __dirname + '/../config/cards/OfficialCanadianExpansion_a.json', 63 | OfficialChristmasExpansionAnswers: __dirname + '/../config/cards/OfficialChristmasExpansion_a.json', 64 | BGGAnswers: __dirname + '/../config/cards/BGG_a.json', 65 | CustomAnswers: __dirname + '/../config/cards/Custom_a.json' 66 | }; 67 | 68 | // Init validator 69 | var validator = new JaySchema(); 70 | // Define schema to calidate against 71 | var schema = { 72 | "$schema": "http://json-schema.org/draft-04/schema#", 73 | "title": "Card Schema", 74 | "type": "array", 75 | "items": { 76 | "title": "Single card", 77 | "type": "object", 78 | "properties": { 79 | "type": { 80 | "description": "Type of the card (question or answer", 81 | "type": "string" 82 | }, 83 | "value": { 84 | "description": "The text value of the card", 85 | "type": "string" 86 | }, 87 | "keep": { 88 | "type": "string" 89 | }, 90 | "draw": { 91 | "description": "Amount of cards that should be drawn from the deck when this card is in play", 92 | "type": "integer" 93 | }, 94 | "pick": { 95 | "description": "Amount of cards that should be picked from the hand when this card is in play", 96 | "type": "integer" 97 | }, 98 | "source": { 99 | "description": "Source of the card (e.g. expansion, community etc)", 100 | "type": "string" 101 | } 102 | }, 103 | "required": ["value", "type", "pick", "draw"] 104 | } 105 | }; 106 | 107 | 108 | // Validate and load cards files 109 | console.log('Loading card data...'); 110 | for (var i in cardFiles) { 111 | if (cardFiles.hasOwnProperty(i)) { 112 | loadCardFile(i, cardFiles[i]); 113 | } 114 | } 115 | 116 | module.exports = config; 117 | -------------------------------------------------------------------------------- /config/cards/OfficialCanadianExpansion_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "A Molson muscle", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official Canadian Expansion" 9 | }, 10 | { 11 | "type": "Answer", 12 | "value": "An icy hand job from an Edmonton hooker", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Canadian Expansion" 17 | }, 18 | { 19 | "type": "Answer", 20 | "value": "Being Canadian", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official Canadian Expansion" 25 | }, 26 | { 27 | "type": "Answer", 28 | "value": "Burning down the White House", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official Canadian Expansion" 33 | }, 34 | { 35 | "type": "Answer", 36 | "value": "Canada: America's hat", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Canadian Expansion" 41 | }, 42 | { 43 | "type": "Answer", 44 | "value": "Don Cherry's wardrobe", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official Canadian Expansion" 49 | }, 50 | { 51 | "type": "Answer", 52 | "value": "Graham Greene playing the same First Nations character on every TV show", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Canadian Expansion" 57 | }, 58 | { 59 | "type": "Answer", 60 | "value": "Heritage minutes", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official Canadian Expansion" 65 | }, 66 | { 67 | "type": "Answer", 68 | "value": "Homo milk", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official Canadian Expansion" 73 | }, 74 | { 75 | "type": "Answer", 76 | "value": "Karla Homolka", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official Canadian Expansion" 81 | }, 82 | { 83 | "type": "Answer", 84 | "value": "Killing a Moose with your bare hands", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official Canadian Expansion" 89 | }, 90 | { 91 | "type": "Answer", 92 | "value": "Mr. Dressup", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official Canadian Expansion" 97 | }, 98 | { 99 | "type": "Answer", 100 | "value": "Naked News", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official Canadian Expansion" 105 | }, 106 | { 107 | "type": "Answer", 108 | "value": "Newfies", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "CaH Official Canadian Expansion" 113 | }, 114 | { 115 | "type": "Answer", 116 | "value": "Poutine", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official Canadian Expansion" 121 | }, 122 | { 123 | "type": "Answer", 124 | "value": "Quintland", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official Canadian Expansion" 129 | }, 130 | { 131 | "type": "Answer", 132 | "value": "Schmirler the Curler", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official Canadian Expansion" 137 | }, 138 | { 139 | "type": "Answer", 140 | "value": "Snotsicles", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "CaH Official Canadian Expansion" 145 | }, 146 | { 147 | "type": "Answer", 148 | "value": "Stephen Harper", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official Canadian Expansion" 153 | }, 154 | { 155 | "type": "Answer", 156 | "value": "Syrupy sex with a maple tree", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official Canadian Expansion" 161 | }, 162 | { 163 | "type": "Answer", 164 | "value": "Terry Fox's prosthetic leg", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official Canadian Expansion" 169 | }, 170 | { 171 | "type": "Answer", 172 | "value": "The CBC", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official Canadian Expansion" 177 | }, 178 | { 179 | "type": "Answer", 180 | "value": "The Famous Five", 181 | "keep": "Yes", 182 | "draw": 0, 183 | "pick": 1, 184 | "source": "CaH Official Canadian Expansion" 185 | }, 186 | { 187 | "type": "Answer", 188 | "value": "The Front de Libération du Québec", 189 | "keep": "Yes", 190 | "draw": 0, 191 | "pick": 1, 192 | "source": "CaH Official Canadian Expansion" 193 | }, 194 | { 195 | "type": "Answer", 196 | "value": "The Offical Languages Act. La Loi sur les langues officielles", 197 | "keep": "Yes", 198 | "draw": 0, 199 | "pick": 1, 200 | "source": "CaH Official Canadian Expansion" 201 | }, 202 | { 203 | "type": "Answer", 204 | "value": "The Royal Canadian Mounted Police", 205 | "keep": "Yes", 206 | "draw": 0, 207 | "pick": 1, 208 | "source": "CaH Official Canadian Expansion" 209 | }, 210 | { 211 | "type": "Answer", 212 | "value": "Tim Hortons", 213 | "keep": "Yes", 214 | "draw": 0, 215 | "pick": 1, 216 | "source": "CaH Official Canadian Expansion" 217 | } 218 | ] -------------------------------------------------------------------------------- /config/cards/OfficialBaseSetAmerica_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "A Bop It", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official Base Set" 9 | }, 10 | { 11 | "type": "Answer", 12 | "value": "Aaron Burr", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Base Set" 17 | }, 18 | { 19 | "type": "Answer", 20 | "value": "Bill Nye the Science Guy", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official Base Set" 25 | }, 26 | { 27 | "type": "Answer", 28 | "value": "Chutzpah", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official Base Set" 33 | }, 34 | { 35 | "type": "Answer", 36 | "value": "Count Chocula", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Base Set" 41 | }, 42 | { 43 | "type": "Answer", 44 | "value": "Domino's Oreo Dessert Pizza", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official Base Set" 49 | }, 50 | { 51 | "type": "Answer", 52 | "value": "Feeding Rosie O'Donnell", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Base Set" 57 | }, 58 | { 59 | "type": "Answer", 60 | "value": "Five-Dollar Footlongs", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official Base Set" 65 | }, 66 | { 67 | "type": "Answer", 68 | "value": "Forgetting the Alamo", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official Base Set" 73 | }, 74 | { 75 | "type": "Answer", 76 | "value": "Former President George W. Bush", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official Base Set" 81 | }, 82 | { 83 | "type": "Answer", 84 | "value": "Glenn Beck being harried by a swarm of buzzards", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official Base Set" 89 | }, 90 | { 91 | "type": "Answer", 92 | "value": "Glenn Beck catching his scrotum on a curtain hook", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official Base Set" 97 | }, 98 | { 99 | "type": "Answer", 100 | "value": "Glenn Beck convulsively vomiting as a brood of crab spiders hatches in his brain and erupts from his tear ducts", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official Base Set" 105 | }, 106 | { 107 | "type": "Answer", 108 | "value": "GoGurt", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "CaH Official Base Set" 113 | }, 114 | { 115 | "type": "Answer", 116 | "value": "Judge Judy", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official Base Set" 121 | }, 122 | { 123 | "type": "Answer", 124 | "value": "Lunchables", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official Base Set" 129 | }, 130 | { 131 | "type": "Answer", 132 | "value": "Mathletes", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official Base Set" 137 | }, 138 | { 139 | "type": "Answer", 140 | "value": "Mr. Clean, right behind you", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "CaH Official Base Set" 145 | }, 146 | { 147 | "type": "Answer", 148 | "value": "One thousand Slim Jims", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official Base Set" 153 | }, 154 | { 155 | "type": "Answer", 156 | "value": "Switching to Geico", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official Base Set" 161 | }, 162 | { 163 | "type": "Answer", 164 | "value": "The Fanta girls", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official Base Set" 169 | }, 170 | { 171 | "type": "Answer", 172 | "value": "The Hamburglar", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official Base Set" 177 | }, 178 | { 179 | "type": "Answer", 180 | "value": "The Kool-Aid Man", 181 | "keep": "Yes", 182 | "draw": 0, 183 | "pick": 1, 184 | "source": "CaH Official Base Set" 185 | }, 186 | { 187 | "type": "Answer", 188 | "value": "The Little Engine That Could", 189 | "keep": "Yes", 190 | "draw": 0, 191 | "pick": 1, 192 | "source": "CaH Official Base Set" 193 | }, 194 | { 195 | "type": "Answer", 196 | "value": "The Make-A-Wish Foundation", 197 | "keep": "Yes", 198 | "draw": 0, 199 | "pick": 1, 200 | "source": "CaH Official Base Set" 201 | }, 202 | { 203 | "type": "Answer", 204 | "value": "The shambling corpse of Larry King", 205 | "keep": "Yes", 206 | "draw": 0, 207 | "pick": 1, 208 | "source": "CaH Official Base Set" 209 | }, 210 | { 211 | "type": "Answer", 212 | "value": "The Tempur-Pedic Swedish Sleep System", 213 | "keep": "Yes", 214 | "draw": 0, 215 | "pick": 1, 216 | "source": "CaH Official Base Set" 217 | }, 218 | { 219 | "type": "Answer", 220 | "value": "Toni Morrison's vagina", 221 | "keep": "Yes", 222 | "draw": 0, 223 | "pick": 1, 224 | "source": "CaH Official Base Set" 225 | } 226 | ] -------------------------------------------------------------------------------- /config/cards/Official3rdExpansion_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "%s. Awesome in theory, kind of a mess in practice.", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 3rd Expansion" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "%s: Hours of fun. Easy to use. Perfect for %s!", 13 | "keep": "Yes", 14 | "draw": 1, 15 | "pick": 2, 16 | "source": "CaH Official 3rd Expansion" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "A successful job interview begins with a firm handshake and ends with %s.", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official 3rd Expansion" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "After months of practice with %s, I think I’m finally ready for %s.", 29 | "keep": "Yes", 30 | "draw": 1, 31 | "pick": 2, 32 | "source": "CaH Official 3rd Expansion" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "And what did you bring for show and tell?", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official 3rd Expansion" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "As part of his contract, Prince won’t perform without %s in his dressing room.", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official 3rd Expansion" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "Call the law offices of Goldstein & Goldstein, because no one should have to tolerate %s in the workplace.", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official 3rd Expansion" 57 | }, 58 | { 59 | "type": "Question", 60 | "value": "During high school I never really fit in until I found %s club.", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official 3rd Expansion" 65 | }, 66 | { 67 | "type": "Question", 68 | "value": "Finally! A service that delivers %s right to your door.", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official 3rd Expansion" 73 | }, 74 | { 75 | "type": "Question", 76 | "value": "Having problems with %s? Try %s!", 77 | "keep": "Yes", 78 | "draw": 1, 79 | "pick": 2, 80 | "source": "CaH Official 3rd Expansion" 81 | }, 82 | { 83 | "type": "Question", 84 | "value": "Hey baby, come back to my place and I’ll show you %s.", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official 3rd Expansion" 89 | }, 90 | { 91 | "type": "Question", 92 | "value": "I’m not like the rest of you. I’m too rich and busy for %s.", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official 3rd Expansion" 97 | }, 98 | { 99 | "type": "Question", 100 | "value": "In the seventh circle of Hell, sinners must endure %s for all eternity.", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official 3rd Expansion" 105 | }, 106 | { 107 | "type": "Question", 108 | "value": "Listen, son. If you want to get involved with %s, I won’t stop you. Just steer clear of %s.", 109 | "keep": "Yes", 110 | "draw": 1, 111 | "pick": 2, 112 | "source": "CaH Official 3rd Expansion" 113 | }, 114 | { 115 | "type": "Question", 116 | "value": "Lovin’ you is easy ’cause you’re %s.", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official 3rd Expansion" 121 | }, 122 | { 123 | "type": "Question", 124 | "value": "Money can’t buy me love, but it can buy me %s.", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official 3rd Expansion" 129 | }, 130 | { 131 | "type": "Question", 132 | "value": "My gym teacher got fired for adding %s to the obstacle course.", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official 3rd Expansion" 137 | }, 138 | { 139 | "type": "Question", 140 | "value": "My life is ruled by a vicious cycle of %s and %s.", 141 | "keep": "Yes", 142 | "draw": 1, 143 | "pick": 2, 144 | "source": "CaH Official 3rd Expansion" 145 | }, 146 | { 147 | "type": "Question", 148 | "value": "The blind date was going horribly until we discovered our shared interest in %s.", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official 3rd Expansion" 153 | }, 154 | { 155 | "type": "Question", 156 | "value": "To prepare for his upcoming role, Daniel Day-Lewis immersed himself in the world of %s.", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official 3rd Expansion" 161 | }, 162 | { 163 | "type": "Question", 164 | "value": "Turns out that %s-Man was neither the hero we needed nor wanted.", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official 3rd Expansion" 169 | }, 170 | { 171 | "type": "Question", 172 | "value": "What left this stain on my couch?", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official 3rd Expansion" 177 | }, 178 | { 179 | "type": "Question", 180 | "value": "When you get right down to it, %s is just %s.", 181 | "keep": "Yes", 182 | "draw": 1, 183 | "pick": 2, 184 | "source": "CaH Official 3rd Expansion" 185 | }, 186 | { 187 | "type": "Question", 188 | "value": "With enough time and pressure, %s will turn into %s.", 189 | "keep": "Yes", 190 | "draw": 1, 191 | "pick": 2, 192 | "source": "CaH Official 3rd Expansion" 193 | } 194 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Cards Against Humanity IRC bot 2 | 3 | IRC bot that let's you play [Cards Against Humanity](http://www.cardsagainsthumanity.com/) in IRC. The game is running in IRCnet on #cah, but you can just as easily run your own instance on your own channel for more private games. 4 | 5 | ##Commands 6 | * **!start #** - Start a new game. Optional parameter can by used to set a point limit for the game (e.g. `!start 10` to play until one player has 10 points.) 7 | * **!stop** - Stop the currently running game. 8 | * **!pause** - Pause the currently running game. 9 | * **!resume** - Resume a paused game. 10 | * **!join** - Join to the currently running game. 11 | * **!quit** - Quit from the game. 12 | * **!cards** - Show the cards you have in your hand. 13 | * **!play # (#)** - Play a card from your hand, # being the number of the card in the list. Play as many numbers separated by spaces as the current card required. 14 | * **!winner #** - Pick a winner of the round, # being the number of the entry in the list. Only for the current *card czar*. 15 | * **!points** - Show players' *awesome points* in the current game. 16 | * **!list** - List players in the current game. 17 | * **!status** - Show current status of the game. Output depends on the state of the game (e.g. when waiting for players to play, you can check who hasn't played yet) 18 | * **!pick** - Alias for !play and !winner commands. 19 | 20 | Some of these commands reply as notice. If you use [Irssi](http://www.irssi.org), you can use [active_notice.pl](http://scripts.irssi.org/scripts/active_notice.pl) to get notices on the active window instead of status window. 21 | 22 | ##Install 23 | 1. Clone the repository. 24 | 2. Edit configuration files with your channel & server settings. 25 | 3. Install dependencies using `npm install`. 26 | 27 | ###Requirements 28 | * Node.js 0.10.* 29 | 30 | ##Run 31 | Run the bot by running `node app.js`, or if you want to run it with production settings instead of development, run `NODE_ENV=production node app.js`. 32 | 33 | ##Configuration 34 | Main configuration files are located in `config/env`. There are two files by default for two different environments, development and production (e.g. if you want to test the bot on a separate channel). For the `clientOptions` directive, refer to the [Node-IRC documentation](https://node-irc.readthedocs.org/en/latest/API.html#client). 35 | 36 | It is possible to configure the bot to send a message to a user or channel after connecting to server or joining a specific channel using `connectCommands` and `joinCommands`. This can be used, for example, to identify with NickServ on networks that require it. See examples below. 37 | 38 | ###Cards 39 | Card configuration is located in `config/cards` directory. Some files are included by default, that contain the default cards of the game plus some extra cards from [BoardGameGeek](http://boardgamegeek.com/). You can add your custom cards to `Custom_a.json` (for answers) and `Custom_q.json` (for questions), using the same format as the default card files. Any card you add to these files will also be automatically loaded to the game during start up.. 40 | 41 | ###Notify Users 42 | Users currently in the channel with the bot can be notified when a game begins by setting the `notifyUsers` directive to true. Users with ~ and & modes are not notified. 43 | 44 | ###Set Topic 45 | The bot can be configured to set the channel topic indicating whether a game is running or not by setting the `setTopic` directive to true. The `topicBase` directive will be appended to the end of the status information. The bot must have permission in the channel for this to work. 46 | 47 | ###Point Limit 48 | You can set a default point limit in the configuration file by settings the `pointLimit` to any positive number. The game stops when a player reaches this point limit. 0 or a negative number means no point limit and games are played until `!stop` command is entered. 49 | 50 | Additionally point limit can be set on a per game basis as a parameter for the `!start` command (see *Commands*). 51 | 52 | ###Connect and join command examples 53 | 54 | ####NickServer 55 | To identify with NickServ after connecting, you can use the following ´connectCommands´: 56 | 57 | ```JavaScript 58 | "connectCommands": [ 59 | { 60 | "target": "nickserv", 61 | "message": "identify " 62 | } 63 | ] 64 | ``` 65 | 66 | ####Notify after connecting and joining #awesomechannel 67 | 68 | This example will send you a private message when the bot has connected to server and another private message when it has joined #awesomechannel. The bot will also send a message to #awesomechannel saying that it's back and ready to play. 69 | 70 | ```JavaScript 71 | "connectCommands": [ 72 | { 73 | "target": "yournick", 74 | "message": "Connected to server." 75 | } 76 | ], 77 | "joinCommands": { 78 | "#awesomechannel": [ 79 | { 80 | "target": "#awesomechannel", 81 | "message" "I'm back, let's play!" 82 | }, 83 | { 84 | "target": "yournick", 85 | "message": "I just joined #awesomechannel." 86 | } 87 | ] 88 | } 89 | ``` 90 | ##TODO 91 | * Save game & player data to MongoDB for all time top scores & other statistics. 92 | * Config options for rule variations, such as voting the best instead of card czar choosing the winner. 93 | * The haiku round. 94 | * Allow players to change one card per round (make it an option in config?) 95 | 96 | ##Contribute 97 | All contributions are welcome in any form, be it pull requests for new features and bug fixes or issue reports or anything else. 98 | 99 | It is recommended to use the **develop** branch as a starting point for new features. 100 | 101 | ##Thanks 102 | Special thanks to everyone on the ***super awesome secret IRC channel*** that have helped me test this and given feedback during development. 103 | 104 | ##License 105 | Cards Against Humanity IRC bot and its source code is licensed under a [Creative Commons BY-NC-SA 2.0 license](http://creativecommons.org/licenses/by-nc-sa/2.0/). 106 | -------------------------------------------------------------------------------- /app/controllers/games.js: -------------------------------------------------------------------------------- 1 | // import modules 2 | var _ = require('underscore'), 3 | Game = require('./game'), 4 | Player = require('../models/player'), 5 | config = require('../../config/config'); 6 | 7 | var Games = function Games() { 8 | var self = this; 9 | self.games = []; 10 | 11 | /** 12 | * Find a game by channel it is running on 13 | * @param channel 14 | * @returns {*} 15 | */ 16 | self.findGame = function (channel) { 17 | return _.findWhere(self.games, {channel: channel}); 18 | }; 19 | 20 | /** 21 | * Start a game 22 | * @param client 23 | * @param message 24 | * @param cmdArgs 25 | */ 26 | self.start = function (client, message, cmdArgs) { 27 | // check if game running on the channel 28 | var channel = message.args[0], 29 | nick = message.nick, 30 | user = message.user, 31 | hostname = message.host; 32 | 33 | if (typeof self.findGame(channel) !== 'undefined') { 34 | // game exists 35 | client.say(channel, 'A game is already running. Type !join to join the game.'); 36 | } else { 37 | // init game 38 | var game = new Game(channel, client, config, cmdArgs); 39 | self.games.push(game); 40 | var player = new Player(nick, user, hostname); 41 | game.addPlayer(player); 42 | } 43 | }; 44 | 45 | /** 46 | * Stop a game 47 | * @param client 48 | * @param message 49 | * @param cmdArgs 50 | */ 51 | self.stop = function (client, message, cmdArgs) { 52 | var channel = message.args[0], 53 | user = message.user, 54 | hostname = message.host, 55 | game = self.findGame(channel); 56 | if (typeof game === 'undefined') { 57 | client.say(channel, 'No game running. Start the game by typing !start.'); 58 | } else { 59 | var player = game.getPlayer({user: user, hostname: hostname}); 60 | if (typeof(player) !== 'undefined') { 61 | game.stop(game.getPlayer({user: user, hostname: hostname})); 62 | self.games = _.without(self.games, game); 63 | } 64 | } 65 | }; 66 | 67 | /** 68 | * Pause a game 69 | * @param client 70 | * @param message 71 | * @param cmdArgs 72 | */ 73 | self.pause = function(client, message, cmdArgs) { 74 | var channel = message.args[0], 75 | nick = message.nick, 76 | user = message.user, 77 | hostname = message.host, 78 | game = self.findGame(channel); 79 | if (typeof game === 'undefined') { 80 | client.say(channel, 'No game running. Start the game by typing !start.'); 81 | } else { 82 | var player = game.getPlayer({user: user, hostname: hostname}); 83 | if (typeof(player) !== 'undefined') { 84 | game.pause(); 85 | } 86 | } 87 | }; 88 | 89 | /** 90 | * Resume a game 91 | * @param client 92 | * @param message 93 | * @param cmdArgs 94 | */ 95 | self.resume = function(client, message, cmdArgs) { 96 | var channel = message.args[0], 97 | nick = message.nick, 98 | user = message.user, 99 | hostname = message.host, 100 | game = self.findGame(channel); 101 | if (typeof game === 'undefined') { 102 | client.say(channel, 'No game running. Start the game by typing !start.'); 103 | } else { 104 | var player = game.getPlayer({user: user, hostname: hostname}); 105 | if (typeof(player) !== 'undefined') { 106 | game.resume(); 107 | } 108 | } 109 | }; 110 | 111 | /** 112 | * Add player to game 113 | * @param client 114 | * @param message 115 | * @param cmdArgs 116 | */ 117 | self.join = function (client, message, cmdArgs) { 118 | var channel = message.args[0], 119 | nick = message.nick, 120 | user = message.user, 121 | hostname = message.host, 122 | game = self.findGame(channel); 123 | 124 | if (typeof game === 'undefined') { 125 | client.say(channel, 'No game running. Start the game by typing !start.'); 126 | } else { 127 | var player = new Player(nick, user, hostname); 128 | game.addPlayer(player); 129 | } 130 | }; 131 | 132 | /** 133 | * Remove player from game 134 | * @param client 135 | * @param message 136 | * @param cmdArgs 137 | */ 138 | self.quit = function (client, message, cmdArgs) { 139 | var channel = message.args[0], 140 | user = message.user, 141 | hostname = message.host, 142 | game = self.findGame(channel); 143 | if (typeof game === 'undefined') { 144 | client.say(channel, 'No game running. Start the game by typing !start.'); 145 | } else { 146 | game.removePlayer(game.getPlayer({user: user, hostname: hostname})); 147 | } 148 | }; 149 | 150 | /** 151 | * Get players cards 152 | * @param client 153 | * @param message 154 | * @param cmdArgs 155 | */ 156 | self.cards = function (client, message, cmdArgs) { 157 | var channel = message.args[0], 158 | user = message.user, 159 | hostname = message.host, 160 | game = self.findGame(channel); 161 | if (typeof game === 'undefined') { 162 | client.say(channel, 'No game running. Start the game by typing !start.'); 163 | } else { 164 | var player = game.getPlayer({user: user, hostname: hostname}); 165 | game.showCards(player); 166 | } 167 | }; 168 | 169 | /** 170 | * Play cards 171 | * @param client 172 | * @param message 173 | * @param cmdArgs 174 | */ 175 | self.play = function (client, message, cmdArgs) { 176 | // check if everyone has played and end the round 177 | var channel = message.args[0], 178 | user = message.user, 179 | hostname = message.host, 180 | game = self.findGame(channel); 181 | if (typeof game === 'undefined') { 182 | client.say(channel, 'No game running. Start the game by typing !start.'); 183 | } else { 184 | var player = game.getPlayer({user: user, hostname: hostname}); 185 | if (typeof(player) !== 'undefined') { 186 | game.playCard(cmdArgs, player); 187 | } 188 | } 189 | }; 190 | 191 | /** 192 | * Lisst players in the game 193 | * @param client 194 | * @param message 195 | * @param cmdArgs 196 | */ 197 | self.list = function (client, message, cmdArgs) { 198 | var channel = message.args[0], 199 | game = self.findGame(channel); 200 | if (typeof game === 'undefined') { 201 | client.say(channel, 'No game running. Start the game by typing !start.'); 202 | } else { 203 | game.listPlayers(); 204 | } 205 | }; 206 | 207 | /** 208 | * Select the winner 209 | * @param client 210 | * @param message 211 | * @param cmdArgs 212 | */ 213 | self.winner = function (client, message, cmdArgs) { 214 | var channel = message.args[0], 215 | user = message.user, 216 | hostname = message.host, 217 | game = self.findGame(channel); 218 | if (typeof game === 'undefined') { 219 | client.say(channel, 'No game running. Start the game by typing !start.'); 220 | } else { 221 | var player = game.getPlayer({user: user, hostname: hostname}); 222 | if (typeof(player) !== 'undefined') { 223 | game.selectWinner(cmdArgs[0], player); 224 | } 225 | } 226 | }; 227 | 228 | /** 229 | * Show top players in current game 230 | * @param client 231 | * @param message 232 | * @param cmdArgs 233 | */ 234 | self.points = function (client, message, cmdArgs) { 235 | var channel = message.args[0], 236 | hostname = message.host, 237 | game = self.findGame(channel); 238 | if (typeof game === 'undefined') { 239 | client.say(channel, 'No game running. Start the game by typing !start.'); 240 | } else { 241 | game.showPoints(); 242 | } 243 | }; 244 | 245 | /** 246 | * Show top players in current game 247 | * @param client 248 | * @param message 249 | * @param cmdArgs 250 | */ 251 | self.status = function(client, message, cmdArgs) { 252 | var channel = message.args[0], 253 | game = self.findGame(channel); 254 | if (typeof game === 'undefined') { 255 | client.say(channel, 'No game running. Start the game by typing !start.'); 256 | } else { 257 | game.showStatus(); 258 | } 259 | }; 260 | 261 | self.pick = function (client, message, cmdArgs) 262 | { 263 | // check if everyone has played and end the round 264 | var channel = message.args[0], 265 | user = message.user, 266 | hostname = message.host, 267 | game = self.findGame(channel); 268 | 269 | if (typeof game === 'undefined'){ 270 | client.say(channel, 'No game running. Start the game by typing !start.'); 271 | } else { 272 | var player = game.getPlayer({user: user, hostname: hostname}); 273 | 274 | if (typeof(player) !== 'undefined') { 275 | if (game.state === Game.STATES.PLAYED) { 276 | game.selectWinner(cmdArgs[0], player); 277 | } else if (game.state === Game.STATES.PLAYABLE) { 278 | game.playCard(cmdArgs, player); 279 | } else { 280 | client.say(channel, '!pick command not available in current state.'); 281 | } 282 | } 283 | } 284 | }; 285 | }; 286 | 287 | exports = module.exports = Games; 288 | -------------------------------------------------------------------------------- /config/cards/Official2ndExpansion_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "A 55-gallon drum of lube", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 2nd Expansion" 9 | }, 10 | { 11 | "type": "Answer", 12 | "value": "A bigger, blacker dick", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official 2nd Expansion" 17 | }, 18 | { 19 | "type": "Answer", 20 | "value": "A Burmese tiger pit", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official 2nd Expansion" 25 | }, 26 | { 27 | "type": "Answer", 28 | "value": "A dollop of sour cream", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official 2nd Expansion" 33 | }, 34 | { 35 | "type": "Answer", 36 | "value": "A magic hippie love cloud", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official 2nd Expansion" 41 | }, 42 | { 43 | "type": "Answer", 44 | "value": "A man in yoga pants with a ponytail and feather earrings", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official 2nd Expansion" 49 | }, 50 | { 51 | "type": "Answer", 52 | "value": "A piñata full of scorpions", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official 2nd Expansion" 57 | }, 58 | { 59 | "type": "Answer", 60 | "value": "A sad fat dragon with no friends", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official 2nd Expansion" 65 | }, 66 | { 67 | "type": "Answer", 68 | "value": "A slightly shittier parallel universe", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official 2nd Expansion" 73 | }, 74 | { 75 | "type": "Answer", 76 | "value": "A soulful rendition of \"Ol' Man River.\"", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official 2nd Expansion" 81 | }, 82 | { 83 | "type": "Answer", 84 | "value": "A squadron of moles wearing aviator goggles", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official 2nd Expansion" 89 | }, 90 | { 91 | "type": "Answer", 92 | "value": "A sweaty, panting leather daddy", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official 2nd Expansion" 97 | }, 98 | { 99 | "type": "Answer", 100 | "value": "A sweet spaceship", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official 2nd Expansion" 105 | }, 106 | { 107 | "type": "Answer", 108 | "value": "All of this blood", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "CaH Official 2nd Expansion" 113 | }, 114 | { 115 | "type": "Answer", 116 | "value": "An army of skeletons", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official 2nd Expansion" 121 | }, 122 | { 123 | "type": "Answer", 124 | "value": "An ether-soaked rag", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official 2nd Expansion" 129 | }, 130 | { 131 | "type": "Answer", 132 | "value": "An unhinged ferris wheel rolling toward the sea", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official 2nd Expansion" 137 | }, 138 | { 139 | "type": "Answer", 140 | "value": "Another shot of morphine", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "CaH Official 2nd Expansion" 145 | }, 146 | { 147 | "type": "Answer", 148 | "value": "Basic human decency", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official 2nd Expansion" 153 | }, 154 | { 155 | "type": "Answer", 156 | "value": "Beefin' over turf", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official 2nd Expansion" 161 | }, 162 | { 163 | "type": "Answer", 164 | "value": "Being awesome at sex", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official 2nd Expansion" 169 | }, 170 | { 171 | "type": "Answer", 172 | "value": "Boris the Soviet Love Hammer", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official 2nd Expansion" 177 | }, 178 | { 179 | "type": "Answer", 180 | "value": "Bullshit", 181 | "keep": "Yes", 182 | "draw": 0, 183 | "pick": 1, 184 | "source": "CaH Official 2nd Expansion" 185 | }, 186 | { 187 | "type": "Answer", 188 | "value": "Catastrophic urethral trauma", 189 | "keep": "Yes", 190 | "draw": 0, 191 | "pick": 1, 192 | "source": "CaH Official 2nd Expansion" 193 | }, 194 | { 195 | "type": "Answer", 196 | "value": "Daddy's belt", 197 | "keep": "Yes", 198 | "draw": 0, 199 | "pick": 1, 200 | "source": "CaH Official 2nd Expansion" 201 | }, 202 | { 203 | "type": "Answer", 204 | "value": "Death by Steven Seagal", 205 | "keep": "Yes", 206 | "draw": 0, 207 | "pick": 1, 208 | "source": "CaH Official 2nd Expansion" 209 | }, 210 | { 211 | "type": "Answer", 212 | "value": "Dining with cardboard cutouts of the cast of \"Friends.\"", 213 | "keep": "Yes", 214 | "draw": 0, 215 | "pick": 1, 216 | "source": "CaH Official 2nd Expansion" 217 | }, 218 | { 219 | "type": "Answer", 220 | "value": "Double penetration", 221 | "keep": "Yes", 222 | "draw": 0, 223 | "pick": 1, 224 | "source": "CaH Official 2nd Expansion" 225 | }, 226 | { 227 | "type": "Answer", 228 | "value": "Existing", 229 | "keep": "Yes", 230 | "draw": 0, 231 | "pick": 1, 232 | "source": "CaH Official 2nd Expansion" 233 | }, 234 | { 235 | "type": "Answer", 236 | "value": "Fetal alcohol syndrome", 237 | "keep": "Yes", 238 | "draw": 0, 239 | "pick": 1, 240 | "source": "CaH Official 2nd Expansion" 241 | }, 242 | { 243 | "type": "Answer", 244 | "value": "Finding Waldo", 245 | "keep": "Yes", 246 | "draw": 0, 247 | "pick": 1, 248 | "source": "CaH Official 2nd Expansion" 249 | }, 250 | { 251 | "type": "Answer", 252 | "value": "Fuck Mountain", 253 | "keep": "Yes", 254 | "draw": 0, 255 | "pick": 1, 256 | "source": "CaH Official 2nd Expansion" 257 | }, 258 | { 259 | "type": "Answer", 260 | "value": "Grandpa's ashes", 261 | "keep": "Yes", 262 | "draw": 0, 263 | "pick": 1, 264 | "source": "CaH Official 2nd Expansion" 265 | }, 266 | { 267 | "type": "Answer", 268 | "value": "Graphic violence, adult language, and some sexual content", 269 | "keep": "Yes", 270 | "draw": 0, 271 | "pick": 1, 272 | "source": "CaH Official 2nd Expansion" 273 | }, 274 | { 275 | "type": "Answer", 276 | "value": "Hillary Clinton's death stare", 277 | "keep": "Yes", 278 | "draw": 0, 279 | "pick": 1, 280 | "source": "CaH Official 2nd Expansion" 281 | }, 282 | { 283 | "type": "Answer", 284 | "value": "Intimacy problems", 285 | "keep": "Yes", 286 | "draw": 0, 287 | "pick": 1, 288 | "source": "CaH Official 2nd Expansion" 289 | }, 290 | { 291 | "type": "Answer", 292 | "value": "Jeff Goldblum", 293 | "keep": "Yes", 294 | "draw": 0, 295 | "pick": 1, 296 | "source": "CaH Official 2nd Expansion" 297 | }, 298 | { 299 | "type": "Answer", 300 | "value": "Living in a trashcan", 301 | "keep": "Yes", 302 | "draw": 0, 303 | "pick": 1, 304 | "source": "CaH Official 2nd Expansion" 305 | }, 306 | { 307 | "type": "Answer", 308 | "value": "Loki, the trickster god", 309 | "keep": "Yes", 310 | "draw": 0, 311 | "pick": 1, 312 | "source": "CaH Official 2nd Expansion" 313 | }, 314 | { 315 | "type": "Answer", 316 | "value": "Making a friend", 317 | "keep": "Yes", 318 | "draw": 0, 319 | "pick": 1, 320 | "source": "CaH Official 2nd Expansion" 321 | }, 322 | { 323 | "type": "Answer", 324 | "value": "Me", 325 | "keep": "Yes", 326 | "draw": 0, 327 | "pick": 1, 328 | "source": "CaH Official 2nd Expansion" 329 | }, 330 | { 331 | "type": "Answer", 332 | "value": "Mild autism", 333 | "keep": "Yes", 334 | "draw": 0, 335 | "pick": 1, 336 | "source": "CaH Official 2nd Expansion" 337 | }, 338 | { 339 | "type": "Answer", 340 | "value": "Mooing", 341 | "keep": "Yes", 342 | "draw": 0, 343 | "pick": 1, 344 | "source": "CaH Official 2nd Expansion" 345 | }, 346 | { 347 | "type": "Answer", 348 | "value": "My first kill", 349 | "keep": "Yes", 350 | "draw": 0, 351 | "pick": 1, 352 | "source": "CaH Official 2nd Expansion" 353 | }, 354 | { 355 | "type": "Answer", 356 | "value": "Nunchuck moves", 357 | "keep": "Yes", 358 | "draw": 0, 359 | "pick": 1, 360 | "source": "CaH Official 2nd Expansion" 361 | }, 362 | { 363 | "type": "Answer", 364 | "value": "Oncoming traffic", 365 | "keep": "Yes", 366 | "draw": 0, 367 | "pick": 1, 368 | "source": "CaH Official 2nd Expansion" 369 | }, 370 | { 371 | "type": "Answer", 372 | "value": "One Ring to rule them all", 373 | "keep": "Yes", 374 | "draw": 0, 375 | "pick": 1, 376 | "source": "CaH Official 2nd Expansion" 377 | }, 378 | { 379 | "type": "Answer", 380 | "value": "Power", 381 | "keep": "Yes", 382 | "draw": 0, 383 | "pick": 1, 384 | "source": "CaH Official 2nd Expansion" 385 | }, 386 | { 387 | "type": "Answer", 388 | "value": "Pretty Pretty Princess Dress-Up Board Game", 389 | "keep": "Yes", 390 | "draw": 0, 391 | "pick": 1, 392 | "source": "CaH Official 2nd Expansion" 393 | }, 394 | { 395 | "type": "Answer", 396 | "value": "Pumping out a baby every nine months", 397 | "keep": "Yes", 398 | "draw": 0, 399 | "pick": 1, 400 | "source": "CaH Official 2nd Expansion" 401 | }, 402 | { 403 | "type": "Answer", 404 | "value": "Rising from the grave", 405 | "keep": "Yes", 406 | "draw": 0, 407 | "pick": 1, 408 | "source": "CaH Official 2nd Expansion" 409 | }, 410 | { 411 | "type": "Answer", 412 | "value": "Scrotal frostbite", 413 | "keep": "Yes", 414 | "draw": 0, 415 | "pick": 1, 416 | "source": "CaH Official 2nd Expansion" 417 | }, 418 | { 419 | "type": "Answer", 420 | "value": "Some really fucked-up shit", 421 | "keep": "Yes", 422 | "draw": 0, 423 | "pick": 1, 424 | "source": "CaH Official 2nd Expansion" 425 | }, 426 | { 427 | "type": "Answer", 428 | "value": "Special musical guest, Cher", 429 | "keep": "Yes", 430 | "draw": 0, 431 | "pick": 1, 432 | "source": "CaH Official 2nd Expansion" 433 | }, 434 | { 435 | "type": "Answer", 436 | "value": "Subduing a grizzly bear and making her your wife", 437 | "keep": "Yes", 438 | "draw": 0, 439 | "pick": 1, 440 | "source": "CaH Official 2nd Expansion" 441 | }, 442 | { 443 | "type": "Answer", 444 | "value": "Survivor's guilt", 445 | "keep": "Yes", 446 | "draw": 0, 447 | "pick": 1, 448 | "source": "CaH Official 2nd Expansion" 449 | }, 450 | { 451 | "type": "Answer", 452 | "value": "Swiftly achieving orgasm", 453 | "keep": "Yes", 454 | "draw": 0, 455 | "pick": 1, 456 | "source": "CaH Official 2nd Expansion" 457 | }, 458 | { 459 | "type": "Answer", 460 | "value": "Taking a man's eyes and balls out and putting his eyes where his balls go and then his balls in the eye holes", 461 | "keep": "Yes", 462 | "draw": 0, 463 | "pick": 1, 464 | "source": "CaH Official 2nd Expansion" 465 | }, 466 | { 467 | "type": "Answer", 468 | "value": "The corporations", 469 | "keep": "Yes", 470 | "draw": 0, 471 | "pick": 1, 472 | "source": "CaH Official 2nd Expansion" 473 | }, 474 | { 475 | "type": "Answer", 476 | "value": "The day the birds attacked", 477 | "keep": "Yes", 478 | "draw": 0, 479 | "pick": 1, 480 | "source": "CaH Official 2nd Expansion" 481 | }, 482 | { 483 | "type": "Answer", 484 | "value": "The Google", 485 | "keep": "Yes", 486 | "draw": 0, 487 | "pick": 1, 488 | "source": "CaH Official 2nd Expansion" 489 | }, 490 | { 491 | "type": "Answer", 492 | "value": "The human body", 493 | "keep": "Yes", 494 | "draw": 0, 495 | "pick": 1, 496 | "source": "CaH Official 2nd Expansion" 497 | }, 498 | { 499 | "type": "Answer", 500 | "value": "The mixing of the races", 501 | "keep": "Yes", 502 | "draw": 0, 503 | "pick": 1, 504 | "source": "CaH Official 2nd Expansion" 505 | }, 506 | { 507 | "type": "Answer", 508 | "value": "The new Radiohead album", 509 | "keep": "Yes", 510 | "draw": 0, 511 | "pick": 1, 512 | "source": "CaH Official 2nd Expansion" 513 | }, 514 | { 515 | "type": "Answer", 516 | "value": "Tiny nipples", 517 | "keep": "Yes", 518 | "draw": 0, 519 | "pick": 1, 520 | "source": "CaH Official 2nd Expansion" 521 | }, 522 | { 523 | "type": "Answer", 524 | "value": "Tongue", 525 | "keep": "Yes", 526 | "draw": 0, 527 | "pick": 1, 528 | "source": "CaH Official 2nd Expansion" 529 | }, 530 | { 531 | "type": "Answer", 532 | "value": "Upgrading homeless people to mobile hotspots", 533 | "keep": "Yes", 534 | "draw": 0, 535 | "pick": 1, 536 | "source": "CaH Official 2nd Expansion" 537 | }, 538 | { 539 | "type": "Answer", 540 | "value": "Weapons-grade plutonium", 541 | "keep": "Yes", 542 | "draw": 0, 543 | "pick": 1, 544 | "source": "CaH Official 2nd Expansion" 545 | }, 546 | { 547 | "type": "Answer", 548 | "value": "Wearing an octopus for a hat", 549 | "keep": "Yes", 550 | "draw": 0, 551 | "pick": 1, 552 | "source": "CaH Official 2nd Expansion" 553 | }, 554 | { 555 | "type": "Answer", 556 | "value": "Whining like a little bitch", 557 | "keep": "Yes", 558 | "draw": 0, 559 | "pick": 1, 560 | "source": "CaH Official 2nd Expansion" 561 | }, 562 | { 563 | "type": "Answer", 564 | "value": "Whipping a disobedient slave", 565 | "keep": "Yes", 566 | "draw": 0, 567 | "pick": 1, 568 | "source": "CaH Official 2nd Expansion" 569 | } 570 | ] -------------------------------------------------------------------------------- /config/cards/Official3rdExpansion_a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Answer", 4 | "value": "A black male in his early 20s, last seen wearing a hoodie", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "CaH Official 3rd Expansion" 9 | }, 10 | { 11 | "type": "Answer", 12 | "value": "A boo-boo", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official 3rd Expansion" 17 | }, 18 | { 19 | "type": "Answer", 20 | "value": "A botched circumcision", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official 3rd Expansion" 25 | }, 26 | { 27 | "type": "Answer", 28 | "value": "A cat video so cute that your eyes roll back and your spine slides out of your anus", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official 3rd Expansion" 33 | }, 34 | { 35 | "type": "Answer", 36 | "value": "A cop who is also a dog", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official 3rd Expansion" 41 | }, 42 | { 43 | "type": "Answer", 44 | "value": "A greased-up Matthew McConaughey", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official 3rd Expansion" 49 | }, 50 | { 51 | "type": "Answer", 52 | "value": "A lamprey swimming up the toilet and latching onto your taint", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official 3rd Expansion" 57 | }, 58 | { 59 | "type": "Answer", 60 | "value": "A pile of squirming bodies", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official 3rd Expansion" 65 | }, 66 | { 67 | "type": "Answer", 68 | "value": "A PowerPoint presentation", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official 3rd Expansion" 73 | }, 74 | { 75 | "type": "Answer", 76 | "value": "A spontaneous conga line", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official 3rd Expansion" 81 | }, 82 | { 83 | "type": "Answer", 84 | "value": "A surprising amount of hair", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official 3rd Expansion" 89 | }, 90 | { 91 | "type": "Answer", 92 | "value": "A vagina that leads to another dimension", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official 3rd Expansion" 97 | }, 98 | { 99 | "type": "Answer", 100 | "value": "Actually getting shot, for real", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official 3rd Expansion" 105 | }, 106 | { 107 | "type": "Answer", 108 | "value": "All my friends dying", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "CaH Official 3rd Expansion" 113 | }, 114 | { 115 | "type": "Answer", 116 | "value": "An all-midget production of Shakespeare’s Richard III", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official 3rd Expansion" 121 | }, 122 | { 123 | "type": "Answer", 124 | "value": "An ass disaster", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official 3rd Expansion" 129 | }, 130 | { 131 | "type": "Answer", 132 | "value": "An unstoppable wave of fire ants", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official 3rd Expansion" 137 | }, 138 | { 139 | "type": "Answer", 140 | "value": "Bill Clinton, naked on a bearskin rug with a saxophone", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "CaH Official 3rd Expansion" 145 | }, 146 | { 147 | "type": "Answer", 148 | "value": "Blood farts", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official 3rd Expansion" 153 | }, 154 | { 155 | "type": "Answer", 156 | "value": "Blowing some dudes in an alley", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official 3rd Expansion" 161 | }, 162 | { 163 | "type": "Answer", 164 | "value": "Buying the right pants to be cool", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official 3rd Expansion" 169 | }, 170 | { 171 | "type": "Answer", 172 | "value": "Chugging a lava lamp", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official 3rd Expansion" 177 | }, 178 | { 179 | "type": "Answer", 180 | "value": "Cock", 181 | "keep": "Yes", 182 | "draw": 0, 183 | "pick": 1, 184 | "source": "CaH Official 3rd Expansion" 185 | }, 186 | { 187 | "type": "Answer", 188 | "value": "Demonic possession", 189 | "keep": "Yes", 190 | "draw": 0, 191 | "pick": 1, 192 | "source": "CaH Official 3rd Expansion" 193 | }, 194 | { 195 | "type": "Answer", 196 | "value": "Disco fever", 197 | "keep": "Yes", 198 | "draw": 0, 199 | "pick": 1, 200 | "source": "CaH Official 3rd Expansion" 201 | }, 202 | { 203 | "type": "Answer", 204 | "value": "Drinking ten 5-hour ENERGYs to get fifty continuous hours of energy", 205 | "keep": "Yes", 206 | "draw": 0, 207 | "pick": 1, 208 | "source": "CaH Official 3rd Expansion" 209 | }, 210 | { 211 | "type": "Answer", 212 | "value": "Dying alone and in pain", 213 | "keep": "Yes", 214 | "draw": 0, 215 | "pick": 1, 216 | "source": "CaH Official 3rd Expansion" 217 | }, 218 | { 219 | "type": "Answer", 220 | "value": "Eating Tom Selleck’s mustache to gain his powers", 221 | "keep": "Yes", 222 | "draw": 0, 223 | "pick": 1, 224 | "source": "CaH Official 3rd Expansion" 225 | }, 226 | { 227 | "type": "Answer", 228 | "value": "Filling every orifice with butterscotch pudding", 229 | "keep": "Yes", 230 | "draw": 0, 231 | "pick": 1, 232 | "source": "CaH Official 3rd Expansion" 233 | }, 234 | { 235 | "type": "Answer", 236 | "value": "Fisting", 237 | "keep": "Yes", 238 | "draw": 0, 239 | "pick": 1, 240 | "source": "CaH Official 3rd Expansion" 241 | }, 242 | { 243 | "type": "Answer", 244 | "value": "Flying robots that kill people", 245 | "keep": "Yes", 246 | "draw": 0, 247 | "pick": 1, 248 | "source": "CaH Official 3rd Expansion" 249 | }, 250 | { 251 | "type": "Answer", 252 | "value": "Gay aliens", 253 | "keep": "Yes", 254 | "draw": 0, 255 | "pick": 1, 256 | "source": "CaH Official 3rd Expansion" 257 | }, 258 | { 259 | "type": "Answer", 260 | "value": "Getting your dick stuck in a Chinese finger trap with another dick", 261 | "keep": "Yes", 262 | "draw": 0, 263 | "pick": 1, 264 | "source": "CaH Official 3rd Expansion" 265 | }, 266 | { 267 | "type": "Answer", 268 | "value": "Girls that always be textin’", 269 | "keep": "Yes", 270 | "draw": 0, 271 | "pick": 1, 272 | "source": "CaH Official 3rd Expansion" 273 | }, 274 | { 275 | "type": "Answer", 276 | "value": "Going around punching people", 277 | "keep": "Yes", 278 | "draw": 0, 279 | "pick": 1, 280 | "source": "CaH Official 3rd Expansion" 281 | }, 282 | { 283 | "type": "Answer", 284 | "value": "Having sex on top of a pizza", 285 | "keep": "Yes", 286 | "draw": 0, 287 | "pick": 1, 288 | "source": "CaH Official 3rd Expansion" 289 | }, 290 | { 291 | "type": "Answer", 292 | "value": "Having shotguns for legs", 293 | "keep": "Yes", 294 | "draw": 0, 295 | "pick": 1, 296 | "source": "CaH Official 3rd Expansion" 297 | }, 298 | { 299 | "type": "Answer", 300 | "value": "Indescribable loneliness", 301 | "keep": "Yes", 302 | "draw": 0, 303 | "pick": 1, 304 | "source": "CaH Official 3rd Expansion" 305 | }, 306 | { 307 | "type": "Answer", 308 | "value": "Jumping out at people", 309 | "keep": "Yes", 310 | "draw": 0, 311 | "pick": 1, 312 | "source": "CaH Official 3rd Expansion" 313 | }, 314 | { 315 | "type": "Answer", 316 | "value": "Letting everyone down", 317 | "keep": "Yes", 318 | "draw": 0, 319 | "pick": 1, 320 | "source": "CaH Official 3rd Expansion" 321 | }, 322 | { 323 | "type": "Answer", 324 | "value": "Mufasa’s death scene", 325 | "keep": "Yes", 326 | "draw": 0, 327 | "pick": 1, 328 | "source": "CaH Official 3rd Expansion" 329 | }, 330 | { 331 | "type": "Answer", 332 | "value": "My manservant, Claude", 333 | "keep": "Yes", 334 | "draw": 0, 335 | "pick": 1, 336 | "source": "CaH Official 3rd Expansion" 337 | }, 338 | { 339 | "type": "Answer", 340 | "value": "Not contributing to society in any meaningful way", 341 | "keep": "Yes", 342 | "draw": 0, 343 | "pick": 1, 344 | "source": "CaH Official 3rd Expansion" 345 | }, 346 | { 347 | "type": "Answer", 348 | "value": "Not having sex", 349 | "keep": "Yes", 350 | "draw": 0, 351 | "pick": 1, 352 | "source": "CaH Official 3rd Expansion" 353 | }, 354 | { 355 | "type": "Answer", 356 | "value": "Nothing", 357 | "keep": "Yes", 358 | "draw": 0, 359 | "pick": 1, 360 | "source": "CaH Official 3rd Expansion" 361 | }, 362 | { 363 | "type": "Answer", 364 | "value": "Putting an entire peanut buter and jelly sandwich into the VCR", 365 | "keep": "Yes", 366 | "draw": 0, 367 | "pick": 1, 368 | "source": "CaH Official 3rd Expansion" 369 | }, 370 | { 371 | "type": "Answer", 372 | "value": "Reverse cowgirl", 373 | "keep": "Yes", 374 | "draw": 0, 375 | "pick": 1, 376 | "source": "CaH Official 3rd Expansion" 377 | }, 378 | { 379 | "type": "Answer", 380 | "value": "Roland the Farter, flatulist to the king", 381 | "keep": "Yes", 382 | "draw": 0, 383 | "pick": 1, 384 | "source": "CaH Official 3rd Expansion" 385 | }, 386 | { 387 | "type": "Answer", 388 | "value": "Running naked through a mall, pissing and shitting everywhere", 389 | "keep": "Yes", 390 | "draw": 0, 391 | "pick": 1, 392 | "source": "CaH Official 3rd Expansion" 393 | }, 394 | { 395 | "type": "Answer", 396 | "value": "Samuel L. Jackson", 397 | "keep": "Yes", 398 | "draw": 0, 399 | "pick": 1, 400 | "source": "CaH Official 3rd Expansion" 401 | }, 402 | { 403 | "type": "Answer", 404 | "value": "Screaming like a maniac", 405 | "keep": "Yes", 406 | "draw": 0, 407 | "pick": 1, 408 | "source": "CaH Official 3rd Expansion" 409 | }, 410 | { 411 | "type": "Answer", 412 | "value": "Self-flagellation", 413 | "keep": "Yes", 414 | "draw": 0, 415 | "pick": 1, 416 | "source": "CaH Official 3rd Expansion" 417 | }, 418 | { 419 | "type": "Answer", 420 | "value": "Shutting the fuck up", 421 | "keep": "Yes", 422 | "draw": 0, 423 | "pick": 1, 424 | "source": "CaH Official 3rd Expansion" 425 | }, 426 | { 427 | "type": "Answer", 428 | "value": "Slapping a racist old lady", 429 | "keep": "Yes", 430 | "draw": 0, 431 | "pick": 1, 432 | "source": "CaH Official 3rd Expansion" 433 | }, 434 | { 435 | "type": "Answer", 436 | "value": "Sneezing, farting, and coming at the same time", 437 | "keep": "Yes", 438 | "draw": 0, 439 | "pick": 1, 440 | "source": "CaH Official 3rd Expansion" 441 | }, 442 | { 443 | "type": "Answer", 444 | "value": "Some douche with an acoustic guitar", 445 | "keep": "Yes", 446 | "draw": 0, 447 | "pick": 1, 448 | "source": "CaH Official 3rd Expansion" 449 | }, 450 | { 451 | "type": "Answer", 452 | "value": "Some kind of bird-man", 453 | "keep": "Yes", 454 | "draw": 0, 455 | "pick": 1, 456 | "source": "CaH Official 3rd Expansion" 457 | }, 458 | { 459 | "type": "Answer", 460 | "value": "Spending lots of money", 461 | "keep": "Yes", 462 | "draw": 0, 463 | "pick": 1, 464 | "source": "CaH Official 3rd Expansion" 465 | }, 466 | { 467 | "type": "Answer", 468 | "value": "That ass", 469 | "keep": "Yes", 470 | "draw": 0, 471 | "pick": 1, 472 | "source": "CaH Official 3rd Expansion" 473 | }, 474 | { 475 | "type": "Answer", 476 | "value": "The entire Internet", 477 | "keep": "Yes", 478 | "draw": 0, 479 | "pick": 1, 480 | "source": "CaH Official 3rd Expansion" 481 | }, 482 | { 483 | "type": "Answer", 484 | "value": "The Harlem Globetrotters", 485 | "keep": "Yes", 486 | "draw": 0, 487 | "pick": 1, 488 | "source": "CaH Official 3rd Expansion" 489 | }, 490 | { 491 | "type": "Answer", 492 | "value": "The Land of Chocolate", 493 | "keep": "Yes", 494 | "draw": 0, 495 | "pick": 1, 496 | "source": "CaH Official 3rd Expansion" 497 | }, 498 | { 499 | "type": "Answer", 500 | "value": "The moist, demanding chasm of his mouth", 501 | "keep": "Yes", 502 | "draw": 0, 503 | "pick": 1, 504 | "source": "CaH Official 3rd Expansion" 505 | }, 506 | { 507 | "type": "Answer", 508 | "value": "The primal, ball-slapping sex your parents are having right now", 509 | "keep": "Yes", 510 | "draw": 0, 511 | "pick": 1, 512 | "source": "CaH Official 3rd Expansion" 513 | }, 514 | { 515 | "type": "Answer", 516 | "value": "The Quesadilla Explosion Salad from Chili’s", 517 | "keep": "Yes", 518 | "draw": 0, 519 | "pick": 1, 520 | "source": "CaH Official 3rd Expansion" 521 | }, 522 | { 523 | "type": "Answer", 524 | "value": "The systematic destruction of an entire people and their way of life", 525 | "keep": "Yes", 526 | "draw": 0, 527 | "pick": 1, 528 | "source": "CaH Official 3rd Expansion" 529 | }, 530 | { 531 | "type": "Answer", 532 | "value": "The thin veneer of situational causality that underlies porn", 533 | "keep": "Yes", 534 | "draw": 0, 535 | "pick": 1, 536 | "source": "CaH Official 3rd Expansion" 537 | }, 538 | { 539 | "type": "Answer", 540 | "value": "The way white people is", 541 | "keep": "Yes", 542 | "draw": 0, 543 | "pick": 1, 544 | "source": "CaH Official 3rd Expansion" 545 | }, 546 | { 547 | "type": "Answer", 548 | "value": "Three months in the hole", 549 | "keep": "Yes", 550 | "draw": 0, 551 | "pick": 1, 552 | "source": "CaH Official 3rd Expansion" 553 | }, 554 | { 555 | "type": "Answer", 556 | "value": "Unlimited soup, salad, and breadsticks", 557 | "keep": "Yes", 558 | "draw": 0, 559 | "pick": 1, 560 | "source": "CaH Official 3rd Expansion" 561 | }, 562 | { 563 | "type": "Answer", 564 | "value": "Velcro", 565 | "keep": "Yes", 566 | "draw": 0, 567 | "pick": 1, 568 | "source": "CaH Official 3rd Expansion" 569 | }, 570 | { 571 | "type": "Answer", 572 | "value": "Vietnam flashbacks", 573 | "keep": "Yes", 574 | "draw": 0, 575 | "pick": 1, 576 | "source": "CaH Official 3rd Expansion" 577 | }, 578 | { 579 | "type": "Answer", 580 | "value": "Vomiting mid-blowjob", 581 | "keep": "Yes", 582 | "draw": 0, 583 | "pick": 1, 584 | "source": "CaH Official 3rd Expansion" 585 | }, 586 | { 587 | "type": "Answer", 588 | "value": "Warm, velvety muppet sex", 589 | "keep": "Yes", 590 | "draw": 0, 591 | "pick": 1, 592 | "source": "CaH Official 3rd Expansion" 593 | } 594 | ] -------------------------------------------------------------------------------- /config/cards/OfficialBaseSet_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "%s is a slippery slope that leads to %s.", 5 | "keep": "Yes", 6 | "draw": 1, 7 | "pick": 2, 8 | "source": "CaH Official Base Set" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "%s: Betcha can't have just one!", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "CaH Official Base Set" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "%s. High five, bro.", 21 | "keep": "Yes", 22 | "draw": 0, 23 | "pick": 1, 24 | "source": "CaH Official Base Set" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "%s. It's a trap!", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "CaH Official Base Set" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "%s. That's how I want to die.", 37 | "keep": "Yes", 38 | "draw": 0, 39 | "pick": 1, 40 | "source": "CaH Official Base Set" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "%s: Good to the last drop.", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "CaH Official Base Set" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "%s: Kid-tested, mother-approved.", 53 | "keep": "Yes", 54 | "draw": 0, 55 | "pick": 1, 56 | "source": "CaH Official Base Set" 57 | }, 58 | { 59 | "type": "Question", 60 | "value": "%s? There's an app for that.", 61 | "keep": "Yes", 62 | "draw": 0, 63 | "pick": 1, 64 | "source": "CaH Official Base Set" 65 | }, 66 | { 67 | "type": "Question", 68 | "value": "A romantic, candlelit dinner would be incomplete without %s.", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "CaH Official Base Set" 73 | }, 74 | { 75 | "type": "Question", 76 | "value": "After Hurricane Katrina, Sean Penn brought %s to the people of New Orleans.", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "CaH Official Base Set" 81 | }, 82 | { 83 | "type": "Question", 84 | "value": "After the earthquake, Sean Penn brought %s to the people of Haiti.", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "CaH Official Base Set" 89 | }, 90 | { 91 | "type": "Question", 92 | "value": "Alternative medicine is now embracing the curative powers of %s.", 93 | "keep": "Yes", 94 | "draw": 0, 95 | "pick": 1, 96 | "source": "CaH Official Base Set" 97 | }, 98 | { 99 | "type": "Question", 100 | "value": "And I would have gotten away with it, too, if it hadn't been for %s.", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "CaH Official Base Set" 105 | }, 106 | { 107 | "type": "Question", 108 | "value": "And the Academy Award for %s goes to %s.", 109 | "keep": "Yes", 110 | "draw": 1, 111 | "pick": 2, 112 | "source": "CaH Official Base Set" 113 | }, 114 | { 115 | "type": "Question", 116 | "value": "Anthropologists have recently discovered a primitive tribe that worships %s.", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "CaH Official Base Set" 121 | }, 122 | { 123 | "type": "Question", 124 | "value": "Betcha can't have just one!", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "CaH Official Base Set" 129 | }, 130 | { 131 | "type": "Question", 132 | "value": "BILLY MAYS HERE FOR %s!", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "CaH Official Base Set" 137 | }, 138 | { 139 | "type": "Question", 140 | "value": "But before I kill you, Mr. Bond, I must show you %s.", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "CaH Official Base Set" 145 | }, 146 | { 147 | "type": "Question", 148 | "value": "Coming to Broadway this season, %s: The Musical.", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "CaH Official Base Set" 153 | }, 154 | { 155 | "type": "Question", 156 | "value": "Dear Abby, I'm having some trouble with %s and would like your advice.", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "CaH Official Base Set" 161 | }, 162 | { 163 | "type": "Question", 164 | "value": "During Picasso's often-overlooked Brown Period, he produced hundreds of paintings of %s.", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "CaH Official Base Set" 169 | }, 170 | { 171 | "type": "Question", 172 | "value": "During sex, I like to think about %s.", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "CaH Official Base Set" 177 | }, 178 | { 179 | "type": "Question", 180 | "value": "For my next trick, I will pull %s out of %s.", 181 | "keep": "Yes", 182 | "draw": 1, 183 | "pick": 2, 184 | "source": "CaH Official Base Set" 185 | }, 186 | { 187 | "type": "Question", 188 | "value": "He who controls %s controls the world.", 189 | "keep": "Yes", 190 | "draw": 0, 191 | "pick": 1, 192 | "source": "CaH Official Base Set" 193 | }, 194 | { 195 | "type": "Question", 196 | "value": "How am I maintaining my relationship status?", 197 | "keep": "Yes", 198 | "draw": 0, 199 | "pick": 1, 200 | "source": "CaH Official Base Set" 201 | }, 202 | { 203 | "type": "Question", 204 | "value": "I do not know with what weapons World War III will be fought, but World War IV will be fought with %s.", 205 | "keep": "Yes", 206 | "draw": 0, 207 | "pick": 1, 208 | "source": "CaH Official Base Set" 209 | }, 210 | { 211 | "type": "Question", 212 | "value": "I drink to forget %s.", 213 | "keep": "Yes", 214 | "draw": 0, 215 | "pick": 1, 216 | "source": "CaH Official Base Set" 217 | }, 218 | { 219 | "type": "Question", 220 | "value": "I got 99 problems but %s ain't one.", 221 | "keep": "Yes", 222 | "draw": 0, 223 | "pick": 1, 224 | "source": "CaH Official Base Set" 225 | }, 226 | { 227 | "type": "Question", 228 | "value": "I learned the hard way that you can't cheer up a grieving friend with %s.", 229 | "keep": "Yes", 230 | "draw": 0, 231 | "pick": 1, 232 | "source": "CaH Official Base Set" 233 | }, 234 | { 235 | "type": "Question", 236 | "value": "I never truly understood %s until I encountered %s.", 237 | "keep": "Yes", 238 | "draw": 1, 239 | "pick": 2, 240 | "source": "CaH Official Base Set" 241 | }, 242 | { 243 | "type": "Question", 244 | "value": "I wish I hadn't lost the instruction manual for %s.", 245 | "keep": "Yes", 246 | "draw": 0, 247 | "pick": 1, 248 | "source": "CaH Official Base Set" 249 | }, 250 | { 251 | "type": "Question", 252 | "value": "I'm sorry, Professor, but I couldn't complete my homework because of %s.", 253 | "keep": "Yes", 254 | "draw": 0, 255 | "pick": 1, 256 | "source": "CaH Official Base Set" 257 | }, 258 | { 259 | "type": "Question", 260 | "value": "In 1,000 years, when paper money is but a distant memory, %s will be our currency.", 261 | "keep": "Yes", 262 | "draw": 0, 263 | "pick": 1, 264 | "source": "CaH Official Base Set" 265 | }, 266 | { 267 | "type": "Question", 268 | "value": "In a world ravaged by %s, our only solace is %s.", 269 | "keep": "Yes", 270 | "draw": 1, 271 | "pick": 2, 272 | "source": "CaH Official Base Set" 273 | }, 274 | { 275 | "type": "Question", 276 | "value": "In an attempt to reach a wider audience, the Smithsonian Museum of Natural History has opened an interactive exhibit on %s.", 277 | "keep": "Yes", 278 | "draw": 0, 279 | "pick": 1, 280 | "source": "CaH Official Base Set" 281 | }, 282 | { 283 | "type": "Question", 284 | "value": "In his new self-produced album, Kanye West raps over the sounds of %s.", 285 | "keep": "Yes", 286 | "draw": 0, 287 | "pick": 1, 288 | "source": "CaH Official Base Set" 289 | }, 290 | { 291 | "type": "Question", 292 | "value": "In his new summer comedy, Rob Schneider is %s trapped in the body of %s.", 293 | "keep": "Yes", 294 | "draw": 1, 295 | "pick": 2, 296 | "source": "CaH Official Base Set" 297 | }, 298 | { 299 | "type": "Question", 300 | "value": "In its new tourism campaign, Detroit proudly proclaims that it has finally eliminated %s.", 301 | "keep": "Yes", 302 | "draw": 0, 303 | "pick": 1, 304 | "source": "CaH Official Base Set" 305 | }, 306 | { 307 | "type": "Question", 308 | "value": "In L.A. County Jail, word is you can trade 200 cigarettes for %s.", 309 | "keep": "Yes", 310 | "draw": 0, 311 | "pick": 1, 312 | "source": "CaH Official Base Set" 313 | }, 314 | { 315 | "type": "Question", 316 | "value": "In M. Night Shyamalan's new movie, Bruce Willis discovers that %s had really been %s all along.", 317 | "keep": "Yes", 318 | "draw": 1, 319 | "pick": 2, 320 | "source": "CaH Official Base Set" 321 | }, 322 | { 323 | "type": "Question", 324 | "value": "In Michael Jackson's final moments, he thought about %s.", 325 | "keep": "Yes", 326 | "draw": 0, 327 | "pick": 1, 328 | "source": "CaH Official Base Set" 329 | }, 330 | { 331 | "type": "Question", 332 | "value": "In Rome, there are whisperings that the Vatican has a secret room devoted to %s.", 333 | "keep": "Yes", 334 | "draw": 0, 335 | "pick": 1, 336 | "source": "CaH Official Base Set" 337 | }, 338 | { 339 | "type": "Question", 340 | "value": "In the distant future, historians will agree that %s marked the beginning of America's decline.", 341 | "keep": "Yes", 342 | "draw": 0, 343 | "pick": 1, 344 | "source": "CaH Official Base Set" 345 | }, 346 | { 347 | "type": "Question", 348 | "value": "Instead of coal, Santa now gives the bad children %s.", 349 | "keep": "Yes", 350 | "draw": 0, 351 | "pick": 1, 352 | "source": "CaH Official Base Set" 353 | }, 354 | { 355 | "type": "Question", 356 | "value": "It's a pity that kids these days are all getting involved with %s.", 357 | "keep": "Yes", 358 | "draw": 0, 359 | "pick": 1, 360 | "source": "CaH Official Base Set" 361 | }, 362 | { 363 | "type": "Question", 364 | "value": "It's a trap!", 365 | "keep": "Yes", 366 | "draw": 0, 367 | "pick": 1, 368 | "source": "CaH Official Base Set" 369 | }, 370 | { 371 | "type": "Question", 372 | "value": "Life for American Indians was forever changed when the White Man introduced them to %s.", 373 | "keep": "Yes", 374 | "draw": 0, 375 | "pick": 1, 376 | "source": "CaH Official Base Set" 377 | }, 378 | { 379 | "type": "Question", 380 | "value": "Life was difficult for cavemen before %s.", 381 | "keep": "Yes", 382 | "draw": 0, 383 | "pick": 1, 384 | "source": "CaH Official Base Set" 385 | }, 386 | { 387 | "type": "Question", 388 | "value": "Maybe she's born with it. Maybe it's %s.", 389 | "keep": "Yes", 390 | "draw": 0, 391 | "pick": 1, 392 | "source": "CaH Official Base Set" 393 | }, 394 | { 395 | "type": "Question", 396 | "value": "MTV's new reality show features eight washed-up celebrities living with %s.", 397 | "keep": "Yes", 398 | "draw": 0, 399 | "pick": 1, 400 | "source": "CaH Official Base Set" 401 | }, 402 | { 403 | "type": "Question", 404 | "value": "Next from J.K. Rowling: Harry Potter and the Chamber of %s.", 405 | "keep": "Yes", 406 | "draw": 0, 407 | "pick": 1, 408 | "source": "CaH Official Base Set" 409 | }, 410 | { 411 | "type": "Question", 412 | "value": "Rumor has it that Vladimir Putin's favorite dish is %s stuffed with %s.", 413 | "keep": "Yes", 414 | "draw": 1, 415 | "pick": 2, 416 | "source": "CaH Official Base Set" 417 | }, 418 | { 419 | "type": "Question", 420 | "value": "Science will never explain the origin of %s.", 421 | "keep": "Yes", 422 | "draw": 0, 423 | "pick": 1, 424 | "source": "CaH Official Base Set" 425 | }, 426 | { 427 | "type": "Question", 428 | "value": "Sorry everyone, I'm just %s.", 429 | "keep": "Yes", 430 | "draw": 0, 431 | "pick": 1, 432 | "source": "CaH Official Base Set" 433 | }, 434 | { 435 | "type": "Question", 436 | "value": "Studies show that lab rats navigate mazes 50% faster after being exposed to %s.", 437 | "keep": "Yes", 438 | "draw": 0, 439 | "pick": 1, 440 | "source": "CaH Official Base Set" 441 | }, 442 | { 443 | "type": "Question", 444 | "value": "That's right, I killed %s. How, you ask? %s.", 445 | "keep": "Yes", 446 | "draw": 1, 447 | "pick": 2, 448 | "source": "CaH Official Base Set" 449 | }, 450 | { 451 | "type": "Question", 452 | "value": "The CIA now interrogates enemy agents by repeatedly subjecting them to %s.", 453 | "keep": "Yes", 454 | "draw": 0, 455 | "pick": 1, 456 | "source": "CaH Official Base Set" 457 | }, 458 | { 459 | "type": "Question", 460 | "value": "The class field trip was completely ruined by %s.", 461 | "keep": "Yes", 462 | "draw": 0, 463 | "pick": 1, 464 | "source": "CaH Official Base Set" 465 | }, 466 | { 467 | "type": "Question", 468 | "value": "The socialist governments of Scandinavia have declared that access to %s is a basic human right.", 469 | "keep": "Yes", 470 | "draw": 0, 471 | "pick": 1, 472 | "source": "CaH Official Base Set" 473 | }, 474 | { 475 | "type": "Question", 476 | "value": "The U.S. has begun airdropping %s to the children of Afghanistan.", 477 | "keep": "Yes", 478 | "draw": 0, 479 | "pick": 1, 480 | "source": "CaH Official Base Set" 481 | }, 482 | { 483 | "type": "Question", 484 | "value": "This is the way the world ends. Not with a bang but with %s.", 485 | "keep": "Yes", 486 | "draw": 0, 487 | "pick": 1, 488 | "source": "CaH Official Base Set" 489 | }, 490 | { 491 | "type": "Question", 492 | "value": "This season on Man vs. Wild, Bear Grylls must survive in the depths of the Amazon with only %s and his wits.", 493 | "keep": "Yes", 494 | "draw": 0, 495 | "pick": 1, 496 | "source": "CaH Official Base Set" 497 | }, 498 | { 499 | "type": "Question", 500 | "value": "War! What is it good for?", 501 | "keep": "Yes", 502 | "draw": 0, 503 | "pick": 1, 504 | "source": "CaH Official Base Set" 505 | }, 506 | { 507 | "type": "Question", 508 | "value": "What am I giving up for Lent?", 509 | "keep": "Yes", 510 | "draw": 0, 511 | "pick": 1, 512 | "source": "CaH Official Base Set" 513 | }, 514 | { 515 | "type": "Question", 516 | "value": "What are my parents hiding from me?", 517 | "keep": "Yes", 518 | "draw": 0, 519 | "pick": 1, 520 | "source": "CaH Official Base Set" 521 | }, 522 | { 523 | "type": "Question", 524 | "value": "What brought the orgy to a grinding halt?", 525 | "keep": "Yes", 526 | "draw": 0, 527 | "pick": 1, 528 | "source": "CaH Official Base Set" 529 | }, 530 | { 531 | "type": "Question", 532 | "value": "What did I bring back from Mexico?", 533 | "keep": "Yes", 534 | "draw": 0, 535 | "pick": 1, 536 | "source": "CaH Official Base Set" 537 | }, 538 | { 539 | "type": "Question", 540 | "value": "What did the US airdrop to the children of Afghanistan?", 541 | "keep": "Yes", 542 | "draw": 0, 543 | "pick": 1, 544 | "source": "CaH Official Base Set" 545 | }, 546 | { 547 | "type": "Question", 548 | "value": "What did Vin Diesel eat for dinner?", 549 | "keep": "Yes", 550 | "draw": 0, 551 | "pick": 1, 552 | "source": "CaH Official Base Set" 553 | }, 554 | { 555 | "type": "Question", 556 | "value": "What do old people smell like?", 557 | "keep": "Yes", 558 | "draw": 0, 559 | "pick": 1, 560 | "source": "CaH Official Base Set" 561 | }, 562 | { 563 | "type": "Question", 564 | "value": "What does Dick Cheney prefer?", 565 | "keep": "Yes", 566 | "draw": 0, 567 | "pick": 1, 568 | "source": "CaH Official Base Set" 569 | }, 570 | { 571 | "type": "Question", 572 | "value": "What don't you want to find in your Chinese food?", 573 | "keep": "Yes", 574 | "draw": 0, 575 | "pick": 1, 576 | "source": "CaH Official Base Set" 577 | }, 578 | { 579 | "type": "Question", 580 | "value": "What ended my last relationship?", 581 | "keep": "Yes", 582 | "draw": 0, 583 | "pick": 1, 584 | "source": "CaH Official Base Set" 585 | }, 586 | { 587 | "type": "Question", 588 | "value": "What gets better with age?", 589 | "keep": "Yes", 590 | "draw": 0, 591 | "pick": 1, 592 | "source": "CaH Official Base Set" 593 | }, 594 | { 595 | "type": "Question", 596 | "value": "What gives me uncontrollable gas?", 597 | "keep": "Yes", 598 | "draw": 0, 599 | "pick": 1, 600 | "source": "CaH Official Base Set" 601 | }, 602 | { 603 | "type": "Question", 604 | "value": "What has been making life difficult at the nudist colony?", 605 | "keep": "Yes", 606 | "draw": 0, 607 | "pick": 1, 608 | "source": "CaH Official Base Set" 609 | }, 610 | { 611 | "type": "Question", 612 | "value": "What helps Obama unwind?", 613 | "keep": "Yes", 614 | "draw": 0, 615 | "pick": 1, 616 | "source": "CaH Official Base Set" 617 | }, 618 | { 619 | "type": "Question", 620 | "value": "What is Batman's guilty pleasure?", 621 | "keep": "Yes", 622 | "draw": 0, 623 | "pick": 1, 624 | "source": "CaH Official Base Set" 625 | }, 626 | { 627 | "type": "Question", 628 | "value": "What never fails to liven up the party?", 629 | "keep": "Yes", 630 | "draw": 0, 631 | "pick": 1, 632 | "source": "CaH Official Base Set" 633 | }, 634 | { 635 | "type": "Question", 636 | "value": "What will always get you laid?", 637 | "keep": "Yes", 638 | "draw": 0, 639 | "pick": 1, 640 | "source": "CaH Official Base Set" 641 | }, 642 | { 643 | "type": "Question", 644 | "value": "What will I bring back in time to convince people that I am a powerful wizard?", 645 | "keep": "Yes", 646 | "draw": 0, 647 | "pick": 1, 648 | "source": "CaH Official Base Set" 649 | }, 650 | { 651 | "type": "Question", 652 | "value": "What would grandma find disturbing, yet oddly charming?", 653 | "keep": "Yes", 654 | "draw": 0, 655 | "pick": 1, 656 | "source": "CaH Official Base Set" 657 | }, 658 | { 659 | "type": "Question", 660 | "value": "What's a girl's best friend?", 661 | "keep": "Yes", 662 | "draw": 0, 663 | "pick": 1, 664 | "source": "CaH Official Base Set" 665 | }, 666 | { 667 | "type": "Question", 668 | "value": "What's my anti-drug?", 669 | "keep": "Yes", 670 | "draw": 0, 671 | "pick": 1, 672 | "source": "CaH Official Base Set" 673 | }, 674 | { 675 | "type": "Question", 676 | "value": "What's my secret power?", 677 | "keep": "Yes", 678 | "draw": 0, 679 | "pick": 1, 680 | "source": "CaH Official Base Set" 681 | }, 682 | { 683 | "type": "Question", 684 | "value": "What's that smell?", 685 | "keep": "Yes", 686 | "draw": 0, 687 | "pick": 1, 688 | "source": "CaH Official Base Set" 689 | }, 690 | { 691 | "type": "Question", 692 | "value": "What's that sound?", 693 | "keep": "Yes", 694 | "draw": 0, 695 | "pick": 1, 696 | "source": "CaH Official Base Set" 697 | }, 698 | { 699 | "type": "Question", 700 | "value": "What's the crustiest?", 701 | "keep": "Yes", 702 | "draw": 0, 703 | "pick": 1, 704 | "source": "CaH Official Base Set" 705 | }, 706 | { 707 | "type": "Question", 708 | "value": "What's the gift that keeps on giving?", 709 | "keep": "Yes", 710 | "draw": 0, 711 | "pick": 1, 712 | "source": "CaH Official Base Set" 713 | }, 714 | { 715 | "type": "Question", 716 | "value": "What's the most emo?", 717 | "keep": "Yes", 718 | "draw": 0, 719 | "pick": 1, 720 | "source": "CaH Official Base Set" 721 | }, 722 | { 723 | "type": "Question", 724 | "value": "What's the new fad diet?", 725 | "keep": "Yes", 726 | "draw": 0, 727 | "pick": 1, 728 | "source": "CaH Official Base Set" 729 | }, 730 | { 731 | "type": "Question", 732 | "value": "What's the next Happy Meal toy?", 733 | "keep": "Yes", 734 | "draw": 0, 735 | "pick": 1, 736 | "source": "CaH Official Base Set" 737 | }, 738 | { 739 | "type": "Question", 740 | "value": "What's the next superhero/sidekick duo?", 741 | "keep": "Yes", 742 | "draw": 0, 743 | "pick": 1, 744 | "source": "CaH Official Base Set" 745 | }, 746 | { 747 | "type": "Question", 748 | "value": "What's there a ton of in heaven?", 749 | "keep": "Yes", 750 | "draw": 0, 751 | "pick": 1, 752 | "source": "CaH Official Base Set" 753 | }, 754 | { 755 | "type": "Question", 756 | "value": "When all else fails, I can always masturbate to %s.", 757 | "keep": "Yes", 758 | "draw": 0, 759 | "pick": 1, 760 | "source": "CaH Official Base Set" 761 | }, 762 | { 763 | "type": "Question", 764 | "value": "When I am a billionaire, I shall erect a 50-foot statue to commemorate %s.", 765 | "keep": "Yes", 766 | "draw": 0, 767 | "pick": 1, 768 | "source": "CaH Official Base Set" 769 | }, 770 | { 771 | "type": "Question", 772 | "value": "When I am President of the United States, I will create the Department of %s.", 773 | "keep": "Yes", 774 | "draw": 0, 775 | "pick": 1, 776 | "source": "CaH Official Base Set" 777 | }, 778 | { 779 | "type": "Question", 780 | "value": "When I pooped, what came out of my butt?", 781 | "keep": "Yes", 782 | "draw": 0, 783 | "pick": 1, 784 | "source": "CaH Official Base Set" 785 | }, 786 | { 787 | "type": "Question", 788 | "value": "When I was tripping on acid, %s turned into %s.", 789 | "keep": "Yes", 790 | "draw": 1, 791 | "pick": 2, 792 | "source": "CaH Official Base Set" 793 | }, 794 | { 795 | "type": "Question", 796 | "value": "When I'm in prison, I'll have %s smuggled in.", 797 | "keep": "Yes", 798 | "draw": 0, 799 | "pick": 1, 800 | "source": "CaH Official Base Set" 801 | }, 802 | { 803 | "type": "Question", 804 | "value": "When Pharaoh remained unmoved, Moses called down a Plague of %s.", 805 | "keep": "Yes", 806 | "draw": 0, 807 | "pick": 1, 808 | "source": "CaH Official Base Set" 809 | }, 810 | { 811 | "type": "Question", 812 | "value": "While the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on %s.", 813 | "keep": "Yes", 814 | "draw": 0, 815 | "pick": 1, 816 | "source": "CaH Official Base Set" 817 | }, 818 | { 819 | "type": "Question", 820 | "value": "White people like %s.", 821 | "keep": "Yes", 822 | "draw": 0, 823 | "pick": 1, 824 | "source": "CaH Official Base Set" 825 | }, 826 | { 827 | "type": "Question", 828 | "value": "Who stole the cookies from the cookie jar?", 829 | "keep": "Yes", 830 | "draw": 0, 831 | "pick": 1, 832 | "source": "CaH Official Base Set" 833 | }, 834 | { 835 | "type": "Question", 836 | "value": "Why am I sticky?", 837 | "keep": "Yes", 838 | "draw": 0, 839 | "pick": 1, 840 | "source": "CaH Official Base Set" 841 | }, 842 | { 843 | "type": "Question", 844 | "value": "Why can't I sleep at night?", 845 | "keep": "Yes", 846 | "draw": 0, 847 | "pick": 1, 848 | "source": "CaH Official Base Set" 849 | }, 850 | { 851 | "type": "Question", 852 | "value": "Why do I hurt all over?", 853 | "keep": "Yes", 854 | "draw": 0, 855 | "pick": 1, 856 | "source": "CaH Official Base Set" 857 | } 858 | ] -------------------------------------------------------------------------------- /app/controllers/game.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | c = require('irc-colors'), 3 | _ = require('underscore'), 4 | Cards = require('../controllers/cards'), 5 | Card = require('../models/card'); 6 | 7 | /** 8 | * Available states for game 9 | * @type {{STOPPED: string, STARTED: string, PLAYABLE: string, PLAYED: string, ROUND_END: string, WAITING: string}} 10 | */ 11 | var STATES = { 12 | STOPPED: 'Stopped', 13 | STARTED: 'Started', 14 | PLAYABLE: 'Playable', 15 | PLAYED: 'Played', 16 | ROUND_END: 'RoundEnd', 17 | WAITING: 'Waiting', 18 | PAUSED: 'Paused' 19 | }; 20 | 21 | // TODO: Implement the ceremonial haiku round that ends the game 22 | var HAIKU = new Card({ 23 | "draw": 2, 24 | "pick": 3, 25 | "value": "(Draw 2, Pick 3) Make a haiku." 26 | }); 27 | 28 | /** 29 | * A single game object that handles all operations in a game 30 | * @param channel The channel the game is running on 31 | * @param client The IRC client object 32 | * @param config Configuration variables 33 | * @param cmdArgs !start command arguments 34 | * @constructor 35 | */ 36 | var Game = function Game(channel, client, config, cmdArgs) { 37 | var self = this; 38 | 39 | // properties 40 | self.waitCount = 0; // number of times waited until enough players 41 | self.round = 0; // round number 42 | self.players = []; // list of players 43 | self.channel = channel; // the channel this game is running on 44 | self.client = client; // reference to the irc client 45 | self.config = config; // configuration data 46 | self.state = STATES.STARTED; // game state storage 47 | self.pauseState = []; // pause state storage 48 | self.points = []; 49 | self.notifyUsersPending = false; 50 | self.pointLimit = 0; // point limit for the game, defaults to 0 (== no limit) 51 | 52 | console.log('Loaded', config.cards.length, 'cards:'); 53 | var questions = _.filter(config.cards, function(card) { 54 | return card.type.toLowerCase() === 'question'; 55 | }); 56 | console.log(questions.length, 'questions'); 57 | var answers = _.filter(config.cards, function(card) { 58 | return card.type.toLowerCase() === 'answer'; 59 | }); 60 | console.log(answers.length, 'answers'); 61 | 62 | // init decks 63 | self.decks = { 64 | question: new Cards(questions), 65 | answer: new Cards(answers) 66 | }; 67 | // init discard piles 68 | self.discards = { 69 | question: new Cards(), 70 | answer: new Cards() 71 | }; 72 | // init table slots 73 | self.table = { 74 | question: null, 75 | answer: [] 76 | }; 77 | // shuffle decks 78 | self.decks.question.shuffle(); 79 | self.decks.answer.shuffle(); 80 | 81 | // parse point limit from configuration file 82 | if(typeof config.pointLimit !== 'undefined' && !isNaN(config.pointLimit)) { 83 | console.log('Set game point limit to ' + config.pointLimit + ' from config'); 84 | self.pointLimit = parseInt(config.pointLimit); 85 | } 86 | // parse point limit from command arguments 87 | if(typeof cmdArgs[0] !== 'undefined' && !isNaN(cmdArgs[0])) { 88 | console.log('Set game point limit to ' + cmdArgs[0] + ' from arguments'); 89 | self.pointLimit = parseInt(cmdArgs[0]); 90 | } 91 | 92 | /** 93 | * Stop game 94 | */ 95 | self.stop = function (player, pointLimitReached) { 96 | self.state = STATES.STOPPED; 97 | 98 | if (typeof player !== 'undefined' && player !== null) { 99 | self.say(player.nick + ' stopped the game.'); 100 | } else if (pointLimitReached !== true) { 101 | self.say('Game has been stopped.'); 102 | } 103 | if(self.round > 1) { 104 | // show points if played more than one round 105 | self.showPoints(); 106 | } 107 | 108 | // clear all timers 109 | clearTimeout(self.startTimeout); 110 | clearTimeout(self.stopTimeout); 111 | clearTimeout(self.turnTimer); 112 | clearTimeout(self.winnerTimer); 113 | 114 | // Destroy game properties 115 | delete self.players; 116 | delete self.config; 117 | delete self.client; 118 | delete self.channel; 119 | delete self.round; 120 | delete self.decks; 121 | delete self.discards; 122 | delete self.table; 123 | 124 | // set topic 125 | self.setTopic(c.bold.yellow('No game is running. Type !start to begin one!')); 126 | }; 127 | 128 | /** 129 | * Pause game 130 | */ 131 | self.pause = function () { 132 | // check if game is already paused 133 | if (self.state === STATES.PAUSED) { 134 | self.say('Game is already paused. Type !resume to begin playing again.'); 135 | return false; 136 | } 137 | 138 | // only allow pause if game is in PLAYABLE or PLAYED state 139 | if (self.state !== STATES.PLAYABLE && self.state !== STATES.PLAYED) { 140 | self.say('The game cannot be paused right now.'); 141 | return false; 142 | } 143 | 144 | // store state and pause game 145 | var now = new Date(); 146 | self.pauseState.state = self.state; 147 | self.pauseState.elapsed = now.getTime() - self.roundStarted.getTime(); 148 | self.state = STATES.PAUSED; 149 | 150 | self.say('Game is now paused. Type !resume to begin playing again.'); 151 | 152 | // clear turn timers 153 | clearTimeout(self.turnTimer); 154 | clearTimeout(self.winnerTimer); 155 | }; 156 | 157 | /** 158 | * Resume game 159 | */ 160 | self.resume = function () { 161 | // make sure game is paused 162 | if (self.state !== STATES.PAUSED) { 163 | self.say('The game is not paused.'); 164 | return false; 165 | } 166 | 167 | // resume game 168 | var now = new Date(); 169 | var newTime = new Date(); 170 | newTime.setTime(now.getTime() - self.pauseState.elapsed); 171 | self.roundStarted = newTime; 172 | self.state = self.pauseState.state; 173 | 174 | self.say('Game has been resumed.'); 175 | 176 | // resume timers 177 | if (self.state === STATES.PLAYED) { 178 | // check if czar quit during pause 179 | if(self.players.indexOf(self.czar) < 0) { 180 | // no czar 181 | self.say('The czar quit the game during pause. I will pick the winner on this round.'); 182 | // select winner 183 | self.selectWinner(Math.round(Math.random() * (self.table.answer.length - 1))); 184 | } else { 185 | self.winnerTimer = setInterval(self.winnerTimerCheck, 10 * 1000); 186 | } 187 | } else if (self.state === STATES.PLAYABLE) { 188 | self.turnTimer = setInterval(self.turnTimerCheck, 10 * 1000); 189 | } 190 | }; 191 | 192 | /** 193 | * Start next round 194 | */ 195 | self.nextRound = function () { 196 | clearTimeout(self.stopTimeout); 197 | // check if any player reached the point limit 198 | if(self.pointLimit > 0) { 199 | var winner = _.findWhere(self.players, {points: self.pointLimit}); 200 | if(winner) { 201 | self.say(winner.nick + ' has the limit of ' + self.pointLimit + ' awesome points and is the winner of the game! Congratulations!'); 202 | self.stop(null, true); 203 | return false; 204 | } 205 | } 206 | 207 | // check that there's enough players in the game 208 | if (self.players.length < 3) { 209 | self.say('Not enough players to start a round (need at least 3). Waiting for others to join. Stopping in 3 minutes if not enough players.'); 210 | self.state = STATES.WAITING; 211 | // stop game if not enough pleyers in 3 minutes 212 | self.stopTimeout = setTimeout(self.stop, 3 * 60 * 1000); 213 | return false; 214 | } 215 | self.round++; 216 | console.log('Starting round ', self.round); 217 | self.setCzar(); 218 | self.deal(); 219 | self.say('Round ' + self.round + '! ' + self.czar.nick + ' is the card czar.'); 220 | self.playQuestion(); 221 | // show cards for all players (except czar) 222 | _.each(self.players, function (player) { 223 | if (player.isCzar !== true) { 224 | self.showCards(player); 225 | } 226 | }); 227 | self.state = STATES.PLAYABLE; 228 | }; 229 | 230 | /** 231 | * Set a new czar 232 | * @returns Player The player object who is the new czar 233 | */ 234 | self.setCzar = function () { 235 | if (self.czar) { 236 | console.log('Old czar:', self.czar.nick); 237 | } 238 | self.czar = self.players[self.players.indexOf(self.czar) + 1] || self.players[0]; 239 | console.log('New czar:', self.czar.nick); 240 | self.czar.isCzar = true; 241 | return self.czar; 242 | }; 243 | 244 | /** 245 | * Deal cards to fill players' hands 246 | */ 247 | self.deal = function () { 248 | _.each(self.players, function (player) { 249 | console.log(player.nick + '(' + player.hostname + ') has ' + player.cards.numCards() + ' cards. Dealing ' + (10 - player.cards.numCards()) + ' cards'); 250 | for (var i = player.cards.numCards(); i < 10; i++) { 251 | self.checkDecks(); 252 | var card = self.decks.answer.pickCards(); 253 | player.cards.addCard(card); 254 | card.owner = player; 255 | } 256 | }, this); 257 | }; 258 | 259 | /** 260 | * Clean up table after round is complete 261 | */ 262 | self.clean = function () { 263 | // move cards from table to discard 264 | self.discards.question.addCard(self.table.question); 265 | self.table.question = null; 266 | // var count = self.table.answer.length; 267 | _.each(self.table.answer, function (cards) { 268 | _.each(cards.getCards(), function (card) { 269 | card.owner = null; 270 | self.discards.answer.addCard(card); 271 | cards.removeCard(card); 272 | }, this); 273 | }, this); 274 | self.table.answer = []; 275 | 276 | // reset players 277 | var removedNicks = []; 278 | _.each(self.players, function (player) { 279 | player.hasPlayed = false; 280 | player.isCzar = false; 281 | // check inactive count & remove after 3 282 | if (player.inactiveRounds >= 3) { 283 | self.removePlayer(player, {silent: true}); 284 | removedNicks.push(player.nick); 285 | } 286 | }); 287 | if (removedNicks.length > 0) { 288 | self.say('Removed inactive players: ' + removedNicks.join(', ')); 289 | } 290 | // reset state 291 | self.state = STATES.STARTED; 292 | }; 293 | 294 | /** 295 | * Play new question card on the table 296 | */ 297 | self.playQuestion = function () { 298 | self.checkDecks(); 299 | var card = self.decks.question.pickCards(); 300 | // replace all instance of %s with underscores for prettier output 301 | var value = card.value.replace(/\%s/g, '___'); 302 | // check if special pick & draw rules 303 | if (card.pick > 1) { 304 | value += c.bold(' [PICK ' + card.pick + ']'); 305 | } 306 | if (card.draw > 0) { 307 | value += c.bold(' [DRAW ' + card.draw + ']'); 308 | } 309 | self.say(c.bold('CARD: ') + value); 310 | self.table.question = card; 311 | // draw cards 312 | if (self.table.question.draw > 0) { 313 | _.each(_.where(self.players, {isCzar: false}), function (player) { 314 | for (var i = 0; i < self.table.question.draw; i++) { 315 | self.checkDecks(); 316 | var c = self.decks.answer.pickCards(); 317 | player.cards.addCard(c); 318 | c.owner = player; 319 | } 320 | }); 321 | } 322 | // start turn timer, check every 10 secs 323 | clearInterval(self.turnTimer); 324 | self.roundStarted = new Date(); 325 | self.turnTimer = setInterval(self.turnTimerCheck, 10 * 1000); 326 | }; 327 | 328 | /** 329 | * Play a answer card from players hand 330 | * @param cards card indexes in players hand 331 | * @param player Player who played the cards 332 | */ 333 | self.playCard = function (cards, player) { 334 | // don't allow if game is paused 335 | if (self.state === STATES.PAUSED) { 336 | self.say('Game is currently paused.'); 337 | return false; 338 | } 339 | 340 | console.log(player.nick + ' played cards', cards.join(', ')); 341 | // make sure different cards are played 342 | cards = _.uniq(cards); 343 | if (self.state !== STATES.PLAYABLE || player.cards.numCards() === 0) { 344 | self.say(player.nick + ': Can\'t play at the moment.'); 345 | } else if (typeof player !== 'undefined') { 346 | if (player.isCzar === true) { 347 | self.say(player.nick + ': You are the card czar. The czar does not play. The czar makes other people do his dirty work.'); 348 | } else { 349 | if (player.hasPlayed === true) { 350 | self.say(player.nick + ': You have already played on this round.'); 351 | } else if (cards.length != self.table.question.pick) { 352 | // invalid card count 353 | self.say(player.nick + ': You must pick ' + self.table.question.pick + ' different cards.'); 354 | } else { 355 | // get played cards 356 | var playerCards; 357 | try { 358 | playerCards = player.cards.pickCards(cards); 359 | } catch (error) { 360 | self.notice(player.nick, 'Invalid card index'); 361 | return false; 362 | } 363 | self.table.answer.push(playerCards); 364 | player.hasPlayed = true; 365 | player.inactiveRounds = 0; 366 | self.notice(player.nick, 'You played: ' + self.getFullEntry(self.table.question, playerCards.getCards())); 367 | // show entries if all players have played 368 | if (self.checkAllPlayed()) { 369 | self.showEntries(); 370 | } 371 | } 372 | } 373 | } else { 374 | console.warn('Invalid player tried to play a card'); 375 | } 376 | }; 377 | 378 | /** 379 | * Check the time that has elapsed since the beinning of the turn. 380 | * End the turn is time limit is up 381 | */ 382 | self.turnTimerCheck = function () { 383 | // check the time 384 | var now = new Date(); 385 | var timeLimit = 3 * 60 * 1000; 386 | var roundElapsed = (now.getTime() - self.roundStarted.getTime()); 387 | console.log('Round elapsed:', roundElapsed, now.getTime(), self.roundStarted.getTime()); 388 | if (roundElapsed >= timeLimit) { 389 | console.log('The round timed out'); 390 | self.say('Time is up!'); 391 | self.markInactivePlayers(); 392 | // show end of turn 393 | self.showEntries(); 394 | } else if (roundElapsed >= timeLimit - (10 * 1000) && roundElapsed < timeLimit) { 395 | // 10s ... 0s left 396 | self.say('10 seconds left!'); 397 | } else if (roundElapsed >= timeLimit - (30 * 1000) && roundElapsed < timeLimit - (20 * 1000)) { 398 | // 30s ... 20s left 399 | self.say('30 seconds left!'); 400 | } else if (roundElapsed >= timeLimit - (60 * 1000) && roundElapsed < timeLimit - (50 * 1000)) { 401 | // 60s ... 50s left 402 | self.say('Hurry up, 1 minute left!'); 403 | self.showStatus(); 404 | } 405 | }; 406 | 407 | /** 408 | * Show the entries 409 | */ 410 | self.showEntries = function () { 411 | // clear round timer 412 | clearInterval(self.turnTimer); 413 | 414 | self.state = STATES.PLAYED; 415 | // Check if 2 or more entries... 416 | if (self.table.answer.length === 0) { 417 | self.say('No one played on this round.'); 418 | // skip directly to next round 419 | self.clean(); 420 | self.nextRound(); 421 | } else if (self.table.answer.length === 1) { 422 | self.say('Only one player played and is the winner by default.'); 423 | self.selectWinner(0); 424 | } else { 425 | self.say('Everyone has played. Here are the entries:'); 426 | // shuffle the entries 427 | self.table.answer = _.shuffle(self.table.answer); 428 | _.each(self.table.answer, function (cards, i) { 429 | self.say(i + ": " + self.getFullEntry(self.table.question, cards.getCards())); 430 | }, this); 431 | // check that czar still exists 432 | var currentCzar = _.findWhere(this.players, {isCzar: true}); 433 | if (typeof currentCzar === 'undefined') { 434 | // no czar, random winner (TODO: Voting?) 435 | self.say('The czar has fled the scene. So I will pick the winner on this round.'); 436 | self.selectWinner(Math.round(Math.random() * (self.table.answer.length - 1))); 437 | } else { 438 | self.say(self.czar.nick + ': Select the winner (!winner )'); 439 | // start turn timer, check every 10 secs 440 | clearInterval(self.winnerTimer); 441 | self.roundStarted = new Date(); 442 | self.winnerTimer = setInterval(self.winnerTimerCheck, 10 * 1000); 443 | } 444 | 445 | } 446 | }; 447 | 448 | /** 449 | * Check the time that has elapsed since the beinning of the winner select. 450 | * End the turn is time limit is up 451 | */ 452 | self.winnerTimerCheck = function () { 453 | // check the time 454 | var now = new Date(); 455 | var timeLimit = 2 * 60 * 1000; 456 | var roundElapsed = (now.getTime() - self.roundStarted.getTime()); 457 | console.log('Winner selection elapsed:', roundElapsed, now.getTime(), self.roundStarted.getTime()); 458 | if (roundElapsed >= timeLimit) { 459 | console.log('the czar is inactive, selecting winner'); 460 | self.say('Time is up. I will pick the winner on this round.'); 461 | // Check czar & remove player after 3 timeouts 462 | self.czar.inactiveRounds++; 463 | // select winner 464 | self.selectWinner(Math.round(Math.random() * (self.table.answer.length - 1))); 465 | } else if (roundElapsed >= timeLimit - (10 * 1000) && roundElapsed < timeLimit) { 466 | // 10s ... 0s left 467 | self.say(self.czar.nick + ': 10 seconds left!'); 468 | } else if (roundElapsed >= timeLimit - (30 * 1000) && roundElapsed < timeLimit - (20 * 1000)) { 469 | // 30s ... 20s left 470 | self.say(self.czar.nick + ': 30 seconds left!'); 471 | } else if (roundElapsed >= timeLimit - (60 * 1000) && roundElapsed < timeLimit - (50 * 1000)) { 472 | // 60s ... 50s left 473 | self.say(self.czar.nick + ': Hurry up, 1 minute left!'); 474 | } 475 | }; 476 | 477 | /** 478 | * Pick an entry that wins the round 479 | * @param index Index of the winning card in table list 480 | * @param player Player who said the command (use null for internal calls, to ignore checking) 481 | */ 482 | self.selectWinner = function (index, player) { 483 | // don't allow if game is paused 484 | if (self.state === STATES.PAUSED) { 485 | self.say('Game is currently paused.'); 486 | return false; 487 | } 488 | 489 | // clear winner timer 490 | clearInterval(self.winnerTimer); 491 | 492 | var winner = self.table.answer[index]; 493 | if (self.state === STATES.PLAYED) { 494 | if (typeof player !== 'undefined' && player !== self.czar) { 495 | client.say(player.nick + ': You are not the card czar. Only the card czar can select the winner'); 496 | } else if (typeof winner === 'undefined') { 497 | self.say('Invalid winner'); 498 | } else { 499 | self.state = STATES.ROUND_END; 500 | var owner = winner.cards[0].owner; 501 | owner.points++; 502 | // update points object 503 | _.findWhere(self.points, {player: owner}).points = owner.points; 504 | // announce winner 505 | self.say(c.bold('Winner is: ') + owner.nick + ' with "' + self.getFullEntry(self.table.question, winner.getCards()) + '" and gets one awesome point! ' + owner.nick + ' has ' + owner.points + ' awesome points.'); 506 | self.clean(); 507 | self.nextRound(); 508 | } 509 | } 510 | }; 511 | 512 | /** 513 | * Get formatted entry 514 | * @param question 515 | * @param answers 516 | * @returns {*|Object|ServerResponse} 517 | */ 518 | self.getFullEntry = function (question, answers) { 519 | var args = [question.value]; 520 | _.each(answers, function (card) { 521 | args.push(card.value); 522 | }, this); 523 | return util.format.apply(this, args); 524 | }; 525 | 526 | /** 527 | * Check if all active players played on the current round 528 | * @returns Boolean true if all players have played 529 | */ 530 | self.checkAllPlayed = function () { 531 | var allPlayed = false; 532 | if (self.getNotPlayed().length === 0) { 533 | allPlayed = true; 534 | } 535 | return allPlayed; 536 | }; 537 | 538 | /** 539 | * Check if decks are empty & reset with discards 540 | */ 541 | self.checkDecks = function () { 542 | // check answer deck 543 | if (self.decks.answer.numCards() === 0) { 544 | console.log('answer deck is empty. reset from discard.'); 545 | self.decks.answer.reset(self.discards.answer.reset()); 546 | self.decks.answer.shuffle(); 547 | } 548 | // check question deck 549 | if (self.decks.question.numCards() === 0) { 550 | console.log('question deck is empty. reset from discard.'); 551 | self.decks.question.reset(self.discards.question.reset()); 552 | self.decks.question.shuffle(); 553 | } 554 | }; 555 | 556 | /** 557 | * Add a player to the game 558 | * @param player Player object containing new player's data 559 | * @returns The new player or false if invalid player 560 | */ 561 | self.addPlayer = function (player) { 562 | if (typeof self.getPlayer({user: player.user, hostname: player.hostname}) === 'undefined') { 563 | self.players.push(player); 564 | self.say(player.nick + ' has joined the game'); 565 | // check if player is returning to game 566 | var pointsPlayer = _.findWhere(self.points, {user: player.user, hostname: player.hostname}); 567 | if (typeof pointsPlayer === 'undefined') { 568 | // new player 569 | self.points.push({ 570 | user: player.user, // user and hostname are used for matching returning players 571 | hostname: player.hostname, 572 | player: player, // reference to player object saved to points object as well 573 | points: 0 574 | }); 575 | } else { 576 | // returning player 577 | pointsPlayer.player = player; 578 | player.points = pointsPlayer.points; 579 | } 580 | // check if waiting for players 581 | if (self.state === STATES.WAITING && self.players.length >= 3) { 582 | // enough players, start the game 583 | self.nextRound(); 584 | } 585 | return player; 586 | } else { 587 | console.log('Player tried to join again', player.nick, player.user, player.hostname); 588 | } 589 | return false; 590 | }; 591 | 592 | /** 593 | * Find player 594 | * @param search 595 | * @returns {*} 596 | */ 597 | self.getPlayer = function (search) { 598 | return _.findWhere(self.players, search); 599 | }; 600 | 601 | /** 602 | * Remove player from game 603 | * @param player 604 | * @param options Extra options 605 | * @returns The removed player or false if invalid player 606 | */ 607 | self.removePlayer = function (player, options) { 608 | options = _.extend({}, options); 609 | if (typeof player !== 'undefined') { 610 | console.log('removing' + player.nick + ' from the game'); 611 | // get cards in hand 612 | var cards = player.cards.reset(); 613 | // remove player 614 | self.players = _.without(self.players, player); 615 | // put player's cards to discard 616 | _.each(cards, function (card) { 617 | console.log('Add card ', card.text, 'to discard'); 618 | self.discards.answer.addCard(card); 619 | }); 620 | if (options.silent !== true) { 621 | self.say(player.nick + ' has left the game'); 622 | } 623 | 624 | // check if remaining players have all player 625 | if (self.state === STATES.PLAYABLE && self.checkAllPlayed()) { 626 | self.showEntries(); 627 | } 628 | 629 | // check czar 630 | if (self.state === STATES.PLAYED && self.czar === player) { 631 | self.say('The czar has fled the scene. So I will pick the winner on this round.'); 632 | self.selectWinner(Math.round(Math.random() * (self.table.answer.length - 1))); 633 | } 634 | 635 | return player; 636 | } 637 | return false; 638 | }; 639 | 640 | /** 641 | * Get all player who have not played 642 | * @returns Array list of Players that have not played 643 | */ 644 | self.getNotPlayed = function () { 645 | return _.where(_.filter(self.players, function (player) { 646 | // check only players with cards (so players who joined in the middle of a round are ignored) 647 | return player.cards.numCards() > 0; 648 | }), {hasPlayed: false, isCzar: false}); 649 | }; 650 | 651 | /** 652 | * Check for inactive players 653 | * @param options 654 | */ 655 | self.markInactivePlayers = function (options) { 656 | _.each(self.getNotPlayed(), function (player) { 657 | player.inactiveRounds++; 658 | }, this); 659 | }; 660 | 661 | /** 662 | * Show players cards to player 663 | * @param player 664 | */ 665 | self.showCards = function (player) { 666 | if (typeof player !== 'undefined') { 667 | var cards = ""; 668 | _.each(player.cards.getCards(), function (card, index) { 669 | cards += c.bold(' [' + index + '] ') + card.value; 670 | }, this); 671 | self.notice(player.nick, 'Your cards are:' + cards); 672 | } 673 | }; 674 | 675 | /** 676 | * Show points for all players 677 | */ 678 | self.showPoints = function () { 679 | var sortedPlayers = _.sortBy(self.points, function (point) { 680 | return -point.player.points; 681 | }); 682 | var output = ""; 683 | _.each(sortedPlayers, function (point) { 684 | output += point.player.nick + " " + point.points + " awesome points, "; 685 | }); 686 | self.say('The most horrible people: ' + output.slice(0, -2)); 687 | }; 688 | 689 | /** 690 | * Show status 691 | */ 692 | self.showStatus = function () { 693 | var playersNeeded = Math.max(0, 3 - self.players.length), // amount of player needed to start the game 694 | timeLeft = 30 - Math.round((new Date().getTime() - self.startTime.getTime()) / 1000), // time left until first round 695 | activePlayers = _.filter(self.players, function (player) { 696 | // only players with cards in hand are active 697 | return player.cards.numCards() > 0; 698 | }), 699 | played = _.where(activePlayers, {isCzar: false, hasPlayed: true}), // players who have already played 700 | notPlayed = _.where(activePlayers, {isCzar: false, hasPlayed: false}); // players who have not played yet 701 | switch (self.state) { 702 | case STATES.PLAYABLE: 703 | self.say(c.bold('Status: ') + self.czar.nick + ' is the czar. Waiting for players to play: ' + _.pluck(notPlayed, 'nick').join(', ')); 704 | break; 705 | case STATES.PLAYED: 706 | self.say(c.bold('Status: ') + 'Waiting for ' + self.czar.nick + ' to select the winner.'); 707 | break; 708 | case STATES.ROUND_END: 709 | self.say(c.bold('Status: ') + 'Round has ended and next one is starting.'); 710 | break; 711 | case STATES.STARTED: 712 | self.say(c.bold('Status: ') + 'Game starts in ' + timeLeft + ' seconds. Need ' + playersNeeded + ' more players to start.'); 713 | break; 714 | case STATES.STOPPED: 715 | self.say(c.bold('Status: ') + 'Game has been stopped.'); 716 | break; 717 | case STATES.WAITING: 718 | self.say(c.bold('Status: ') + 'Not enough players to start. Need ' + playersNeeded + ' more players to start.'); 719 | break; 720 | case STATES.PAUSED: 721 | self.say(c.bold('Status: ') + 'Game is paused.'); 722 | break; 723 | } 724 | }; 725 | 726 | /** 727 | * Set the channel topic 728 | */ 729 | self.setTopic = function (topic) { 730 | // ignore if not configured to set topic 731 | if (typeof config.setTopic === 'undefined' || !config.setTopic) { 732 | return false; 733 | } 734 | 735 | // construct new topic 736 | var newTopic = topic; 737 | if (typeof config.topicBase !== 'undefined') { 738 | newTopic = topic + ' ' + config.topicBase; 739 | } 740 | 741 | // set it 742 | client.send('TOPIC', channel, newTopic); 743 | }; 744 | 745 | /** 746 | * List all players in the current game 747 | */ 748 | self.listPlayers = function () { 749 | self.say('Players currently in the game: ' + _.pluck(self.players, 'nick').join(', ')); 750 | }; 751 | 752 | /** 753 | * Handle player quits and parts 754 | * @param channel 755 | * @param nick 756 | * @param reason 757 | * @param message 758 | */ 759 | self.playerLeaveHandler = function (channel, nick, reason, message) { 760 | console.log('Player ' + nick + ' left'); 761 | var player = self.getPlayer({nick: nick}); 762 | if (typeof player !== 'undefined') { 763 | self.removePlayer(player); 764 | } 765 | }; 766 | 767 | /** 768 | * Handle player nick changes 769 | * @param oldnick 770 | * @param newnick 771 | * @param channels 772 | * @param message 773 | */ 774 | self.playerNickChangeHandler = function (oldnick, newnick, channels, message) { 775 | console.log('Player changed nick from ' + oldnick + ' to ' + newnick); 776 | var player = self.getPlayer({nick: oldnick}); 777 | if (typeof player !== 'undefined') { 778 | player.nick = newnick; 779 | } 780 | }; 781 | 782 | /** 783 | * Notify users in channel that game has started 784 | */ 785 | self.notifyUsers = function() { 786 | // request names 787 | client.send('NAMES', channel); 788 | 789 | // signal handler to send notifications 790 | self.notifyUsersPending = true; 791 | }; 792 | 793 | /** 794 | * Handle names response to notify users 795 | * @param nicks 796 | */ 797 | self.notifyUsersHandler = function(nicks) { 798 | // ignore if we haven't requested this 799 | if (self.notifyUsersPending === false) { 800 | return false; 801 | } 802 | 803 | // don't message nicks with these modes 804 | var exemptModes = ['~', '&']; 805 | 806 | // loop through and send messages 807 | _.each(nicks, function(mode, nick) { 808 | if (_.indexOf(exemptModes, mode) < 0 && nick !== config.nick) { 809 | self.notice(nick, nick + ': A new game of Cards Against Humanity just began in ' + channel + '. Head over and !join if you\'d like to get in on the fun!'); 810 | } 811 | }); 812 | 813 | // reset 814 | self.notifyUsersPending = false; 815 | }; 816 | 817 | /** 818 | * Public message to the game channel 819 | * @param string 820 | */ 821 | self.say = function (string) { 822 | self.client.say(self.channel, string); 823 | }; 824 | 825 | self.pm = function (nick, string) { 826 | self.client.say(nick, string); 827 | }; 828 | 829 | self.notice = function (nick, string) { 830 | self.client.notice(nick, string); 831 | }; 832 | 833 | // set topic 834 | self.setTopic(c.bold.lime('A game is running. Type !join to get in on it!')); 835 | 836 | // announce the game on the channel 837 | self.say('A new game of ' + c.rainbow('Cards Against Humanity') + '. The game starts in 30 seconds. Type !join to join the game any time.'); 838 | 839 | // notify users 840 | if (typeof config.notifyUsers !== 'undefined' && config.notifyUsers) { 841 | self.notifyUsers(); 842 | } 843 | 844 | // wait for players to join 845 | self.startTime = new Date(); 846 | self.startTimeout = setTimeout(self.nextRound, 30000); 847 | 848 | // client listeners 849 | client.addListener('part', self.playerLeaveHandler); 850 | client.addListener('quit', self.playerLeaveHandler); 851 | client.addListener('nick', self.playerNickChangeHandler); 852 | client.addListener('names'+channel, self.notifyUsersHandler); 853 | }; 854 | 855 | // export static state constant 856 | Game.STATES = STATES; 857 | 858 | exports = module.exports = Game; 859 | -------------------------------------------------------------------------------- /config/cards/BGG_q.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "Question", 4 | "value": "\"Watson!\" Holmes exclaimed, \"This man was obviously killed by %s!\"", 5 | "keep": "Yes", 6 | "draw": 0, 7 | "pick": 1, 8 | "source": "BGG Suggestions" 9 | }, 10 | { 11 | "type": "Question", 12 | "value": "%s ain't nothin' to fuck with.", 13 | "keep": "Yes", 14 | "draw": 0, 15 | "pick": 1, 16 | "source": "BGG Suggestions" 17 | }, 18 | { 19 | "type": "Question", 20 | "value": "%s gives a whole new meaning to %s.", 21 | "keep": "Yes", 22 | "draw": 1, 23 | "pick": 2, 24 | "source": "BGG Suggestions" 25 | }, 26 | { 27 | "type": "Question", 28 | "value": "%s: good to the last drop.", 29 | "keep": "Yes", 30 | "draw": 0, 31 | "pick": 1, 32 | "source": "BGG Suggestions" 33 | }, 34 | { 35 | "type": "Question", 36 | "value": "%s in the front seat; %s in the back seat; gotta make my mind up, which seat do I take?", 37 | "keep": "Yes", 38 | "draw": 1, 39 | "pick": 2, 40 | "source": "BGG Suggestions" 41 | }, 42 | { 43 | "type": "Question", 44 | "value": "%s in the morning, sailors take warning.", 45 | "keep": "Yes", 46 | "draw": 0, 47 | "pick": 1, 48 | "source": "BGG Suggestions" 49 | }, 50 | { 51 | "type": "Question", 52 | "value": "%s is a classic case of %s.", 53 | "keep": "Yes", 54 | "draw": 1, 55 | "pick": 2, 56 | "source": "BGG Suggestions" 57 | }, 58 | { 59 | "type": "Question", 60 | "value": "%s is a gateway to %s.", 61 | "keep": "Yes", 62 | "draw": 1, 63 | "pick": 2, 64 | "source": "BGG Suggestions" 65 | }, 66 | { 67 | "type": "Question", 68 | "value": "%s is a time-saving godsend for busy housewives.", 69 | "keep": "Yes", 70 | "draw": 0, 71 | "pick": 1, 72 | "source": "BGG Suggestions" 73 | }, 74 | { 75 | "type": "Question", 76 | "value": "%s is best enjoyed on the first date.", 77 | "keep": "Yes", 78 | "draw": 0, 79 | "pick": 1, 80 | "source": "BGG Suggestions" 81 | }, 82 | { 83 | "type": "Question", 84 | "value": "%s is dead to me.", 85 | "keep": "Yes", 86 | "draw": 0, 87 | "pick": 1, 88 | "source": "BGG Suggestions" 89 | }, 90 | { 91 | "type": "Question", 92 | "value": "%s is nothing %s can't fix.", 93 | "keep": "Yes", 94 | "draw": 1, 95 | "pick": 2, 96 | "source": "BGG Suggestions" 97 | }, 98 | { 99 | "type": "Question", 100 | "value": "%s is now outsourced to call centers in India.", 101 | "keep": "Yes", 102 | "draw": 0, 103 | "pick": 1, 104 | "source": "BGG Suggestions" 105 | }, 106 | { 107 | "type": "Question", 108 | "value": "%s is the name of my new indie black metal emo jazz post-dubstep funk band.", 109 | "keep": "Yes", 110 | "draw": 0, 111 | "pick": 1, 112 | "source": "BGG Suggestions" 113 | }, 114 | { 115 | "type": "Question", 116 | "value": "%s is the new black.", 117 | "keep": "Yes", 118 | "draw": 0, 119 | "pick": 1, 120 | "source": "BGG Suggestions" 121 | }, 122 | { 123 | "type": "Question", 124 | "value": "%s is the root of all evil.", 125 | "keep": "Yes", 126 | "draw": 0, 127 | "pick": 1, 128 | "source": "BGG Suggestions" 129 | }, 130 | { 131 | "type": "Question", 132 | "value": "%s isn't about sex, it's about power.", 133 | "keep": "Yes", 134 | "draw": 0, 135 | "pick": 1, 136 | "source": "BGG Suggestions" 137 | }, 138 | { 139 | "type": "Question", 140 | "value": "%s means never having to say you're sorry.", 141 | "keep": "Yes", 142 | "draw": 0, 143 | "pick": 1, 144 | "source": "BGG Suggestions" 145 | }, 146 | { 147 | "type": "Question", 148 | "value": "%s R Us.", 149 | "keep": "Yes", 150 | "draw": 0, 151 | "pick": 1, 152 | "source": "BGG Suggestions" 153 | }, 154 | { 155 | "type": "Question", 156 | "value": "%s, for the win!", 157 | "keep": "Yes", 158 | "draw": 0, 159 | "pick": 1, 160 | "source": "BGG Suggestions" 161 | }, 162 | { 163 | "type": "Question", 164 | "value": "%s, fun for the whole family!", 165 | "keep": "Yes", 166 | "draw": 0, 167 | "pick": 1, 168 | "source": "BGG Suggestions" 169 | }, 170 | { 171 | "type": "Question", 172 | "value": "%s. Because, fuck you, that's why.", 173 | "keep": "Yes", 174 | "draw": 0, 175 | "pick": 1, 176 | "source": "BGG Suggestions" 177 | }, 178 | { 179 | "type": "Question", 180 | "value": "%s. I'd hit that.", 181 | "keep": "Yes", 182 | "draw": 0, 183 | "pick": 1, 184 | "source": "BGG Suggestions" 185 | }, 186 | { 187 | "type": "Question", 188 | "value": "%s. It's everywhere you want to be.", 189 | "keep": "Yes", 190 | "draw": 0, 191 | "pick": 1, 192 | "source": "BGG Suggestions" 193 | }, 194 | { 195 | "type": "Question", 196 | "value": "%s. The Care Bear Stare", 197 | "keep": "Yes", 198 | "draw": 0, 199 | "pick": 1, 200 | "source": "BGG Suggestions" 201 | }, 202 | { 203 | "type": "Question", 204 | "value": "%s. The Final Frontier.", 205 | "keep": "Yes", 206 | "draw": 0, 207 | "pick": 1, 208 | "source": "BGG Suggestions" 209 | }, 210 | { 211 | "type": "Question", 212 | "value": "%s. The Nicolas Cage Story", 213 | "keep": "Yes", 214 | "draw": 0, 215 | "pick": 1, 216 | "source": "BGG Suggestions" 217 | }, 218 | { 219 | "type": "Question", 220 | "value": "%s: The Other White Meat.", 221 | "keep": "Yes", 222 | "draw": 0, 223 | "pick": 1, 224 | "source": "BGG Suggestions" 225 | }, 226 | { 227 | "type": "Question", 228 | "value": "9 out of 10 doctors agree, %s twice a day prevents cancer.", 229 | "keep": "Yes", 230 | "draw": 0, 231 | "pick": 1, 232 | "source": "BGG Suggestions" 233 | }, 234 | { 235 | "type": "Question", 236 | "value": "A fortune teller told me I will live a life filled with %s.", 237 | "keep": "Yes", 238 | "draw": 0, 239 | "pick": 1, 240 | "source": "BGG Suggestions" 241 | }, 242 | { 243 | "type": "Question", 244 | "value": "A hot new alternative medical practice involves placing %s in %s.", 245 | "keep": "Yes", 246 | "draw": 1, 247 | "pick": 2, 248 | "source": "BGG Suggestions" 249 | }, 250 | { 251 | "type": "Question", 252 | "value": "A little-known fact is that Microsoft Windows was originally slated to be the first operating system to include %s.", 253 | "keep": "Yes", 254 | "draw": 0, 255 | "pick": 1, 256 | "source": "BGG Suggestions" 257 | }, 258 | { 259 | "type": "Question", 260 | "value": "A little-known provision of Obamacare allows %s access to %s.", 261 | "keep": "Yes", 262 | "draw": 1, 263 | "pick": 2, 264 | "source": "BGG Suggestions" 265 | }, 266 | { 267 | "type": "Question", 268 | "value": "According to an old Romanian superstition, %s will happen if you don't %s before going to bed.", 269 | "keep": "Yes", 270 | "draw": 1, 271 | "pick": 2, 272 | "source": "BGG Suggestions" 273 | }, 274 | { 275 | "type": "Question", 276 | "value": "All the cool kids are doing it.", 277 | "keep": "Yes", 278 | "draw": 0, 279 | "pick": 1, 280 | "source": "BGG Suggestions" 281 | }, 282 | { 283 | "type": "Question", 284 | "value": "An international tribunal has found %s guilty of %s.", 285 | "keep": "Yes", 286 | "draw": 1, 287 | "pick": 2, 288 | "source": "BGG Suggestions" 289 | }, 290 | { 291 | "type": "Question", 292 | "value": "And on his farm he had %s, e-i-e-i-o!", 293 | "keep": "Yes", 294 | "draw": 0, 295 | "pick": 1, 296 | "source": "BGG Suggestions" 297 | }, 298 | { 299 | "type": "Question", 300 | "value": "And then little Billy woke up the next morning, he discovered that the tooth fairy had left %s under his pillow.", 301 | "keep": "Yes", 302 | "draw": 0, 303 | "pick": 1, 304 | "source": "BGG Suggestions" 305 | }, 306 | { 307 | "type": "Question", 308 | "value": "Apple has revealed they now have a patent for %s.", 309 | "keep": "Yes", 310 | "draw": 0, 311 | "pick": 1, 312 | "source": "BGG Suggestions" 313 | }, 314 | { 315 | "type": "Question", 316 | "value": "Ask your doctor if %s is right for you.", 317 | "keep": "Yes", 318 | "draw": 0, 319 | "pick": 1, 320 | "source": "BGG Suggestions" 321 | }, 322 | { 323 | "type": "Question", 324 | "value": "Back in my day, %s only cost a nickel.", 325 | "keep": "Yes", 326 | "draw": 0, 327 | "pick": 1, 328 | "source": "BGG Suggestions" 329 | }, 330 | { 331 | "type": "Question", 332 | "value": "Bang %s Marry %s Kill %s.", 333 | "keep": "Yes", 334 | "draw": 2, 335 | "pick": 3, 336 | "source": "BGG Suggestions" 337 | }, 338 | { 339 | "type": "Question", 340 | "value": "Choosy moms choose %s.", 341 | "keep": "Yes", 342 | "draw": 0, 343 | "pick": 1, 344 | "source": "BGG Suggestions" 345 | }, 346 | { 347 | "type": "Question", 348 | "value": "Cirque de Soleil presents MYSTIQUEE, a night of daring acrobatics, whimsy and %s.", 349 | "keep": "Yes", 350 | "draw": 0, 351 | "pick": 1, 352 | "source": "BGG Suggestions" 353 | }, 354 | { 355 | "type": "Question", 356 | "value": "Daddy drinks because of %s.", 357 | "keep": "Yes", 358 | "draw": 0, 359 | "pick": 1, 360 | "source": "BGG Suggestions" 361 | }, 362 | { 363 | "type": "Question", 364 | "value": "Damn it feels good to be %s.", 365 | "keep": "Yes", 366 | "draw": 0, 367 | "pick": 1, 368 | "source": "BGG Suggestions" 369 | }, 370 | { 371 | "type": "Question", 372 | "value": "Danger! %s ahead!", 373 | "keep": "Yes", 374 | "draw": 0, 375 | "pick": 1, 376 | "source": "BGG Suggestions" 377 | }, 378 | { 379 | "type": "Question", 380 | "value": "Dear Sir or Madam, We regret to inform you that the Office of %s has denied your request for %s.", 381 | "keep": "Yes", 382 | "draw": 1, 383 | "pick": 2, 384 | "source": "BGG Suggestions" 385 | }, 386 | { 387 | "type": "Question", 388 | "value": "Disney presents %s on ice!", 389 | "keep": "Yes", 390 | "draw": 0, 391 | "pick": 1, 392 | "source": "BGG Suggestions" 393 | }, 394 | { 395 | "type": "Question", 396 | "value": "Disney's newest Pixar movie will be about %s.", 397 | "keep": "Yes", 398 | "draw": 0, 399 | "pick": 1, 400 | "source": "BGG Suggestions" 401 | }, 402 | { 403 | "type": "Question", 404 | "value": "For campaign funding reform to really be effective, there needs to be a specific policy about %s contributions.", 405 | "keep": "Yes", 406 | "draw": 0, 407 | "pick": 1, 408 | "source": "BGG Suggestions" 409 | }, 410 | { 411 | "type": "Question", 412 | "value": "The Westboro Baptist Church have just announced that they plan to start picketing the funerals of %s too.", 413 | "keep": "Yes", 414 | "draw": 0, 415 | "pick": 1, 416 | "source": "BGG Suggestions" 417 | }, 418 | { 419 | "type": "Question", 420 | "value": "Genius is 10% inspiration and 90% %s.", 421 | "keep": "Yes", 422 | "draw": 0, 423 | "pick": 1, 424 | "source": "BGG Suggestions" 425 | }, 426 | { 427 | "type": "Question", 428 | "value": "Give me liberty, or give me %s!", 429 | "keep": "Yes", 430 | "draw": 0, 431 | "pick": 1, 432 | "source": "BGG Suggestions" 433 | }, 434 | { 435 | "type": "Question", 436 | "value": "Go-go Gadget %s!", 437 | "keep": "Yes", 438 | "draw": 0, 439 | "pick": 1, 440 | "source": "BGG Suggestions" 441 | }, 442 | { 443 | "type": "Question", 444 | "value": "Groundbreaking research provides convincing evidence that %s killed the dinosaurs.", 445 | "keep": "Yes", 446 | "draw": 0, 447 | "pick": 1, 448 | "source": "BGG Suggestions" 449 | }, 450 | { 451 | "type": "Question", 452 | "value": "Happiness is %s.", 453 | "keep": "Yes", 454 | "draw": 0, 455 | "pick": 1, 456 | "source": "BGG Suggestions" 457 | }, 458 | { 459 | "type": "Question", 460 | "value": "Hey Rocky, watch me pull %s out of my %s!", 461 | "keep": "Yes", 462 | "draw": 1, 463 | "pick": 2, 464 | "source": "BGG Suggestions" 465 | }, 466 | { 467 | "type": "Question", 468 | "value": "How did this %s get into my %s?", 469 | "keep": "Yes", 470 | "draw": 1, 471 | "pick": 2, 472 | "source": "BGG Suggestions" 473 | }, 474 | { 475 | "type": "Question", 476 | "value": "I always call in sick to work when I have a bad case of %s.", 477 | "keep": "Yes", 478 | "draw": 0, 479 | "pick": 1, 480 | "source": "BGG Suggestions" 481 | }, 482 | { 483 | "type": "Question", 484 | "value": "I always think of %s when I fall asleep.", 485 | "keep": "Yes", 486 | "draw": 0, 487 | "pick": 1, 488 | "source": "BGG Suggestions" 489 | }, 490 | { 491 | "type": "Question", 492 | "value": "I can drive and %s at the same time.", 493 | "keep": "Yes", 494 | "draw": 0, 495 | "pick": 1, 496 | "source": "BGG Suggestions" 497 | }, 498 | { 499 | "type": "Question", 500 | "value": "I can handle public displays of affection, but %s is really too much.", 501 | "keep": "Yes", 502 | "draw": 0, 503 | "pick": 1, 504 | "source": "BGG Suggestions" 505 | }, 506 | { 507 | "type": "Question", 508 | "value": "I can't get no %s.", 509 | "keep": "Yes", 510 | "draw": 0, 511 | "pick": 1, 512 | "source": "BGG Suggestions" 513 | }, 514 | { 515 | "type": "Question", 516 | "value": "I feel the need - the need for %s.", 517 | "keep": "Yes", 518 | "draw": 0, 519 | "pick": 1, 520 | "source": "BGG Suggestions" 521 | }, 522 | { 523 | "type": "Question", 524 | "value": "I firmly believe that for the economy to recover, the Department of %s needs to change its policies on %s.", 525 | "keep": "Yes", 526 | "draw": 1, 527 | "pick": 2, 528 | "source": "BGG Suggestions" 529 | }, 530 | { 531 | "type": "Question", 532 | "value": "I for one welcome our new %s overlords!", 533 | "keep": "Yes", 534 | "draw": 0, 535 | "pick": 1, 536 | "source": "BGG Suggestions" 537 | }, 538 | { 539 | "type": "Question", 540 | "value": "I got a fever and the only cure is more %s.", 541 | "keep": "Yes", 542 | "draw": 0, 543 | "pick": 1, 544 | "source": "BGG Suggestions" 545 | }, 546 | { 547 | "type": "Question", 548 | "value": "I have decided to commit to a life of studious contemplation on %s.", 549 | "keep": "Yes", 550 | "draw": 0, 551 | "pick": 1, 552 | "source": "BGG Suggestions" 553 | }, 554 | { 555 | "type": "Question", 556 | "value": "I like my women like %s.", 557 | "keep": "Yes", 558 | "draw": 0, 559 | "pick": 1, 560 | "source": "BGG Suggestions" 561 | }, 562 | { 563 | "type": "Question", 564 | "value": "I like to keep %s as a souvenir of my sexual encounters.", 565 | "keep": "Yes", 566 | "draw": 0, 567 | "pick": 1, 568 | "source": "BGG Suggestions" 569 | }, 570 | { 571 | "type": "Question", 572 | "value": "I live my life according to the teachings of %s.", 573 | "keep": "Yes", 574 | "draw": 0, 575 | "pick": 1, 576 | "source": "BGG Suggestions" 577 | }, 578 | { 579 | "type": "Question", 580 | "value": "I love %s in my %s.", 581 | "keep": "Yes", 582 | "draw": 1, 583 | "pick": 2, 584 | "source": "BGG Suggestions" 585 | }, 586 | { 587 | "type": "Question", 588 | "value": "I love the smell of %s in the morning.", 589 | "keep": "Yes", 590 | "draw": 0, 591 | "pick": 1, 592 | "source": "BGG Suggestions" 593 | }, 594 | { 595 | "type": "Question", 596 | "value": "I met my current significant other at %s,", 597 | "keep": "Yes", 598 | "draw": 0, 599 | "pick": 1, 600 | "source": "BGG Suggestions" 601 | }, 602 | { 603 | "type": "Question", 604 | "value": "I might be a %s, but at least I'm no %s.", 605 | "keep": "Yes", 606 | "draw": 1, 607 | "pick": 2, 608 | "source": "BGG Suggestions" 609 | }, 610 | { 611 | "type": "Question", 612 | "value": "I really hate %s at work. So I take care of it with %s.", 613 | "keep": "Yes", 614 | "draw": 1, 615 | "pick": 2, 616 | "source": "BGG Suggestions" 617 | }, 618 | { 619 | "type": "Question", 620 | "value": "I sleep like a baby after bathing in a tub full of %s.", 621 | "keep": "Yes", 622 | "draw": 0, 623 | "pick": 1, 624 | "source": "BGG Suggestions" 625 | }, 626 | { 627 | "type": "Question", 628 | "value": "I was into %s before it was cool.", 629 | "keep": "Yes", 630 | "draw": 0, 631 | "pick": 1, 632 | "source": "BGG Suggestions" 633 | }, 634 | { 635 | "type": "Question", 636 | "value": " I will not eat %s. I will not eat them Sam-I-Am.", 637 | "keep": "Yes", 638 | "draw": 0, 639 | "pick": 1, 640 | "source": "BGG Suggestions" 641 | }, 642 | { 643 | "type": "Question", 644 | "value": "I work out so that I can look good when I'm %s.", 645 | "keep": "Yes", 646 | "draw": 0, 647 | "pick": 1, 648 | "source": "BGG Suggestions" 649 | }, 650 | { 651 | "type": "Question", 652 | "value": "I'd rather be %s.", 653 | "keep": "Yes", 654 | "draw": 0, 655 | "pick": 1, 656 | "source": "BGG Suggestions" 657 | }, 658 | { 659 | "type": "Question", 660 | "value": "If a pot of gold is on one end of the rainbow, what is on the other?", 661 | "keep": "Yes", 662 | "draw": 0, 663 | "pick": 1, 664 | "source": "BGG Suggestions" 665 | }, 666 | { 667 | "type": "Question", 668 | "value": "If I don't enjoy %s, then the terrorists win.", 669 | "keep": "Yes", 670 | "draw": 0, 671 | "pick": 1, 672 | "source": "BGG Suggestions" 673 | }, 674 | { 675 | "type": "Question", 676 | "value": "If I weren't so busy with %s, I'd be %s.", 677 | "keep": "Yes", 678 | "draw": 1, 679 | "pick": 2, 680 | "source": "BGG Suggestions" 681 | }, 682 | { 683 | "type": "Question", 684 | "value": "If there's one thing you could change about me it would be %s.", 685 | "keep": "Yes", 686 | "draw": 0, 687 | "pick": 1, 688 | "source": "BGG Suggestions" 689 | }, 690 | { 691 | "type": "Question", 692 | "value": "In a pinch, %s can be substituted with %s.", 693 | "keep": "Yes", 694 | "draw": 1, 695 | "pick": 2, 696 | "source": "BGG Suggestions" 697 | }, 698 | { 699 | "type": "Question", 700 | "value": "In a surprising turnaround, %s has publicly come out as supporting President Obama.", 701 | "keep": "Yes", 702 | "draw": 0, 703 | "pick": 1, 704 | "source": "BGG Suggestions" 705 | }, 706 | { 707 | "type": "Question", 708 | "value": "In an astonishing and ground-breaking decision, the Supreme Court has decided in favor of %s.", 709 | "keep": "Yes", 710 | "draw": 0, 711 | "pick": 1, 712 | "source": "BGG Suggestions" 713 | }, 714 | { 715 | "type": "Question", 716 | "value": "In Nintendo's new game, you must fight off wave after wave of %s in order to save %s.", 717 | "keep": "Yes", 718 | "draw": 1, 719 | "pick": 2, 720 | "source": "BGG Suggestions" 721 | }, 722 | { 723 | "type": "Question", 724 | "value": "In the grim darkness of the future there is only %s.", 725 | "keep": "Yes", 726 | "draw": 0, 727 | "pick": 1, 728 | "source": "BGG Suggestions" 729 | }, 730 | { 731 | "type": "Question", 732 | "value": "In the new tenth circle of Hell, sinners will be condemned to an eternity of %s.", 733 | "keep": "Yes", 734 | "draw": 0, 735 | "pick": 1, 736 | "source": "BGG Suggestions" 737 | }, 738 | { 739 | "type": "Question", 740 | "value": "Instead of her normal self, Cinderella should have turned into %s at midnight.", 741 | "keep": "Yes", 742 | "draw": 0, 743 | "pick": 1, 744 | "source": "BGG Suggestions" 745 | }, 746 | { 747 | "type": "Question", 748 | "value": "Instead of Robin, Batman should have had %s as a sidekick.", 749 | "keep": "Yes", 750 | "draw": 0, 751 | "pick": 1, 752 | "source": "BGG Suggestions" 753 | }, 754 | { 755 | "type": "Question", 756 | "value": "Instead of the Jews, Hitler should have worried more about %s.", 757 | "keep": "Yes", 758 | "draw": 0, 759 | "pick": 1, 760 | "source": "BGG Suggestions" 761 | }, 762 | { 763 | "type": "Question", 764 | "value": "Iron Chef: Today's secret ingredient is %s.", 765 | "keep": "Yes", 766 | "draw": 0, 767 | "pick": 1, 768 | "source": "BGG Suggestions" 769 | }, 770 | { 771 | "type": "Question", 772 | "value": "It is often argued that our ancestors would have never evolved without the aid of %s.", 773 | "keep": "Yes", 774 | "draw": 0, 775 | "pick": 1, 776 | "source": "BGG Suggestions" 777 | }, 778 | { 779 | "type": "Question", 780 | "value": "It rubs %s on its skin or else it gets %s again.", 781 | "keep": "Yes", 782 | "draw": 1, 783 | "pick": 2, 784 | "source": "BGG Suggestions" 785 | }, 786 | { 787 | "type": "Question", 788 | "value": "It sucks when you look in the fridge, and all you have to eat is %s.", 789 | "keep": "Yes", 790 | "draw": 0, 791 | "pick": 1, 792 | "source": "BGG Suggestions" 793 | }, 794 | { 795 | "type": "Question", 796 | "value": "It was more uncomfortable than %s at a %s convention.", 797 | "keep": "Yes", 798 | "draw": 1, 799 | "pick": 2, 800 | "source": "BGG Suggestions" 801 | }, 802 | { 803 | "type": "Question", 804 | "value": "I've got the fever and the only cure is more %s.", 805 | "keep": "Yes", 806 | "draw": 0, 807 | "pick": 1, 808 | "source": "BGG Suggestions" 809 | }, 810 | { 811 | "type": "Question", 812 | "value": "Keep your friends close, but your %s closer.", 813 | "keep": "Yes", 814 | "draw": 0, 815 | "pick": 1, 816 | "source": "BGG Suggestions" 817 | }, 818 | { 819 | "type": "Question", 820 | "value": "Kitten thinks of nothing but %s all day.", 821 | "keep": "Yes", 822 | "draw": 0, 823 | "pick": 1, 824 | "source": "BGG Suggestions" 825 | }, 826 | { 827 | "type": "Question", 828 | "value": "Legend of Zelda: The %s of %s.", 829 | "keep": "Yes", 830 | "draw": 1, 831 | "pick": 2, 832 | "source": "BGG Suggestions" 833 | }, 834 | { 835 | "type": "Question", 836 | "value": "Like a good neighbor %s is there.", 837 | "keep": "Yes", 838 | "draw": 0, 839 | "pick": 1, 840 | "source": "BGG Suggestions" 841 | }, 842 | { 843 | "type": "Question", 844 | "value": "Michael Bay's new three-hour action epic pits %s against %s.", 845 | "keep": "Yes", 846 | "draw": 1, 847 | "pick": 2, 848 | "source": "BGG Suggestions" 849 | }, 850 | { 851 | "type": "Question", 852 | "value": "Moral at work was boosted when they put %s in the vending machines.", 853 | "keep": "Yes", 854 | "draw": 0, 855 | "pick": 1, 856 | "source": "BGG Suggestions" 857 | }, 858 | { 859 | "type": "Question", 860 | "value": "My favorite book is the classic tale of %s, written by %s.", 861 | "keep": "Yes", 862 | "draw": 1, 863 | "pick": 2, 864 | "source": "BGG Suggestions" 865 | }, 866 | { 867 | "type": "Question", 868 | "value": "My favorite childhood memory is %s.", 869 | "keep": "Yes", 870 | "draw": 0, 871 | "pick": 1, 872 | "source": "BGG Suggestions" 873 | }, 874 | { 875 | "type": "Question", 876 | "value": "My favorite TV channel is the one that shows programs about %s.", 877 | "keep": "Yes", 878 | "draw": 0, 879 | "pick": 1, 880 | "source": "BGG Suggestions" 881 | }, 882 | { 883 | "type": "Question", 884 | "value": "My favourite family recipe is %s and %s with just a dash of %s.", 885 | "keep": "Yes", 886 | "draw": 2, 887 | "pick": 3, 888 | "source": "BGG Suggestions" 889 | }, 890 | { 891 | "type": "Question", 892 | "value": "My Little Pony: Friendship is %s.", 893 | "keep": "Yes", 894 | "draw": 0, 895 | "pick": 1, 896 | "source": "BGG Suggestions" 897 | }, 898 | { 899 | "type": "Question", 900 | "value": "My mission in life is to create a videogame about %s.", 901 | "keep": "Yes", 902 | "draw": 0, 903 | "pick": 1, 904 | "source": "BGG Suggestions" 905 | }, 906 | { 907 | "type": "Question", 908 | "value": "My other car is a %s.", 909 | "keep": "Yes", 910 | "draw": 0, 911 | "pick": 1, 912 | "source": "BGG Suggestions" 913 | }, 914 | { 915 | "type": "Question", 916 | "value": "New car sticker craze: Calvin pissing on %s.", 917 | "keep": "Yes", 918 | "draw": 0, 919 | "pick": 1, 920 | "source": "BGG Suggestions" 921 | }, 922 | { 923 | "type": "Question", 924 | "value": "Next on Oprah, teens addicted to %s.", 925 | "keep": "Yes", 926 | "draw": 0, 927 | "pick": 1, 928 | "source": "BGG Suggestions" 929 | }, 930 | { 931 | "type": "Question", 932 | "value": "Next, on Martha Stewart's Living, spruce up your %s with %s.", 933 | "keep": "Yes", 934 | "draw": 1, 935 | "pick": 2, 936 | "source": "BGG Suggestions" 937 | }, 938 | { 939 | "type": "Question", 940 | "value": "Next, on the Twilight Zone: a parallel universe where %s is replaced by %s.", 941 | "keep": "Yes", 942 | "draw": 1, 943 | "pick": 2, 944 | "source": "BGG Suggestions" 945 | }, 946 | { 947 | "type": "Question", 948 | "value": "Nothing beats cuddling with %s in bed.", 949 | "keep": "Yes", 950 | "draw": 0, 951 | "pick": 1, 952 | "source": "BGG Suggestions" 953 | }, 954 | { 955 | "type": "Question", 956 | "value": "Nothing feels better than %s on %s.", 957 | "keep": "Yes", 958 | "draw": 1, 959 | "pick": 2, 960 | "source": "BGG Suggestions" 961 | }, 962 | { 963 | "type": "Question", 964 | "value": "Now witness the firepower of this fully armed and operational %s!", 965 | "keep": "Yes", 966 | "draw": 0, 967 | "pick": 1, 968 | "source": "BGG Suggestions" 969 | }, 970 | { 971 | "type": "Question", 972 | "value": "On tonight's special report, %s. Fact or fiction?", 973 | "keep": "Yes", 974 | "draw": 0, 975 | "pick": 1, 976 | "source": "BGG Suggestions" 977 | }, 978 | { 979 | "type": "Question", 980 | "value": "One day a man is bitten by %s and is transformed into %s.", 981 | "keep": "Yes", 982 | "draw": 1, 983 | "pick": 2, 984 | "source": "BGG Suggestions" 985 | }, 986 | { 987 | "type": "Question", 988 | "value": "Only %s is worse than a heretic.", 989 | "keep": "Yes", 990 | "draw": 0, 991 | "pick": 1, 992 | "source": "BGG Suggestions" 993 | }, 994 | { 995 | "type": "Question", 996 | "value": "Our most powerful weapon on the day of the zombie apocalypse will be %s.", 997 | "keep": "Yes", 998 | "draw": 0, 999 | "pick": 1, 1000 | "source": "BGG Suggestions" 1001 | }, 1002 | { 1003 | "type": "Question", 1004 | "value": "Remember, %s is never funny. Unless it involves %s.", 1005 | "keep": "Yes", 1006 | "draw": 1, 1007 | "pick": 2, 1008 | "source": "BGG Suggestions" 1009 | }, 1010 | { 1011 | "type": "Question", 1012 | "value": "Roses are red; Violets are %s; %s is %s, and so are you.", 1013 | "keep": "Yes", 1014 | "draw": 2, 1015 | "pick": 3, 1016 | "source": "BGG Suggestions" 1017 | }, 1018 | { 1019 | "type": "Question", 1020 | "value": "Remember kids, just say no to %s.", 1021 | "keep": "Yes", 1022 | "draw": 0, 1023 | "pick": 1, 1024 | "source": "BGG Suggestions" 1025 | }, 1026 | { 1027 | "type": "Question", 1028 | "value": "Science has discovered that beyond the edges of the universe, there is only %s.", 1029 | "keep": "Yes", 1030 | "draw": 0, 1031 | "pick": 1, 1032 | "source": "BGG Suggestions" 1033 | }, 1034 | { 1035 | "type": "Question", 1036 | "value": "Scientists have reverse engineered alien technology that unlocks the secrets of %s.", 1037 | "keep": "Yes", 1038 | "draw": 0, 1039 | "pick": 1, 1040 | "source": "BGG Suggestions" 1041 | }, 1042 | { 1043 | "type": "Question", 1044 | "value": "Something you should regret, but secretly don't.", 1045 | "keep": "Yes", 1046 | "draw": 0, 1047 | "pick": 1, 1048 | "source": "BGG Suggestions" 1049 | }, 1050 | { 1051 | "type": "Question", 1052 | "value": "Sometimes you feel like %s, sometimes you don't.", 1053 | "keep": "Yes", 1054 | "draw": 0, 1055 | "pick": 1, 1056 | "source": "BGG Suggestions" 1057 | }, 1058 | { 1059 | "type": "Question", 1060 | "value": "Sticks and stones may break my bones, but %s will never hurt me.", 1061 | "keep": "Yes", 1062 | "draw": 0, 1063 | "pick": 1, 1064 | "source": "BGG Suggestions" 1065 | }, 1066 | { 1067 | "type": "Question", 1068 | "value": "Stop staring at %s.", 1069 | "keep": "Yes", 1070 | "draw": 0, 1071 | "pick": 1, 1072 | "source": "BGG Suggestions" 1073 | }, 1074 | { 1075 | "type": "Question", 1076 | "value": "The best Christmas present my grandma ever gave me was %s.", 1077 | "keep": "Yes", 1078 | "draw": 0, 1079 | "pick": 1, 1080 | "source": "BGG Suggestions" 1081 | }, 1082 | { 1083 | "type": "Question", 1084 | "value": "The best things in life come in %s.", 1085 | "keep": "Yes", 1086 | "draw": 0, 1087 | "pick": 1, 1088 | "source": "BGG Suggestions" 1089 | }, 1090 | { 1091 | "type": "Question", 1092 | "value": "The clown made all the children laugh with his balloon animal in the shape of %s.", 1093 | "keep": "Yes", 1094 | "draw": 0, 1095 | "pick": 1, 1096 | "source": "BGG Suggestions" 1097 | }, 1098 | { 1099 | "type": "Question", 1100 | "value": "The Himalayas are filled with many perils, such as %s.", 1101 | "keep": "Yes", 1102 | "draw": 0, 1103 | "pick": 1, 1104 | "source": "BGG Suggestions" 1105 | }, 1106 | { 1107 | "type": "Question", 1108 | "value": "The internet was invented for %s.", 1109 | "keep": "Yes", 1110 | "draw": 0, 1111 | "pick": 1, 1112 | "source": "BGG Suggestions" 1113 | }, 1114 | { 1115 | "type": "Question", 1116 | "value": "The Japanese government spent billions of yen researching %s.", 1117 | "keep": "Yes", 1118 | "draw": 0, 1119 | "pick": 1, 1120 | "source": "BGG Suggestions" 1121 | }, 1122 | { 1123 | "type": "Question", 1124 | "value": "The local experimental college is now offering a course on %s led by %s.", 1125 | "keep": "Yes", 1126 | "draw": 1, 1127 | "pick": 2, 1128 | "source": "BGG Suggestions" 1129 | }, 1130 | { 1131 | "type": "Question", 1132 | "value": "The new hip trend at craft bars is vodka infused with %s.", 1133 | "keep": "Yes", 1134 | "draw": 0, 1135 | "pick": 1, 1136 | "source": "BGG Suggestions" 1137 | }, 1138 | { 1139 | "type": "Question", 1140 | "value": "The new Nintendo will be called the %s.", 1141 | "keep": "Yes", 1142 | "draw": 0, 1143 | "pick": 1, 1144 | "source": "BGG Suggestions" 1145 | }, 1146 | { 1147 | "type": "Question", 1148 | "value": "The new version of Dungeons and Dragons makes %s a playable race.", 1149 | "keep": "Yes", 1150 | "draw": 0, 1151 | "pick": 1, 1152 | "source": "BGG Suggestions" 1153 | }, 1154 | { 1155 | "type": "Question", 1156 | "value": "The next ice cream flavor from Ben & Jerry's.", 1157 | "keep": "Yes", 1158 | "draw": 0, 1159 | "pick": 1, 1160 | "source": "BGG Suggestions" 1161 | }, 1162 | { 1163 | "type": "Question", 1164 | "value": "The next Team Fortress 2 update will include %s.", 1165 | "keep": "Yes", 1166 | "draw": 0, 1167 | "pick": 1, 1168 | "source": "BGG Suggestions" 1169 | }, 1170 | { 1171 | "type": "Question", 1172 | "value": "The only problem with %s is that there isn't enough %s in it.", 1173 | "keep": "Yes", 1174 | "draw": 1, 1175 | "pick": 2, 1176 | "source": "BGG Suggestions" 1177 | }, 1178 | { 1179 | "type": "Question", 1180 | "value": "The power of love is useless against %s.", 1181 | "keep": "Yes", 1182 | "draw": 0, 1183 | "pick": 1, 1184 | "source": "BGG Suggestions" 1185 | }, 1186 | { 1187 | "type": "Question", 1188 | "value": "The road to success is paved with %s.", 1189 | "keep": "Yes", 1190 | "draw": 0, 1191 | "pick": 1, 1192 | "source": "BGG Suggestions" 1193 | }, 1194 | { 1195 | "type": "Question", 1196 | "value": "The sad truth is, at the edge of the universe, there is nothing but %s.", 1197 | "keep": "Yes", 1198 | "draw": 0, 1199 | "pick": 1, 1200 | "source": "BGG Suggestions" 1201 | }, 1202 | { 1203 | "type": "Question", 1204 | "value": "The theme for our senior prom will be %s.", 1205 | "keep": "Yes", 1206 | "draw": 0, 1207 | "pick": 1, 1208 | "source": "BGG Suggestions" 1209 | }, 1210 | { 1211 | "type": "Question", 1212 | "value": "There is a time for peace, a time for war, and a time for %s.", 1213 | "keep": "Yes", 1214 | "draw": 0, 1215 | "pick": 1, 1216 | "source": "BGG Suggestions" 1217 | }, 1218 | { 1219 | "type": "Question", 1220 | "value": "There is nothing worse than seeing %s in the light of day.", 1221 | "keep": "Yes", 1222 | "draw": 0, 1223 | "pick": 1, 1224 | "source": "BGG Suggestions" 1225 | }, 1226 | { 1227 | "type": "Question", 1228 | "value": "There's no crying in %s.", 1229 | "keep": "Yes", 1230 | "draw": 0, 1231 | "pick": 1, 1232 | "source": "BGG Suggestions" 1233 | }, 1234 | { 1235 | "type": "Question", 1236 | "value": "There's no such thing as %s.", 1237 | "keep": "Yes", 1238 | "draw": 0, 1239 | "pick": 1, 1240 | "source": "BGG Suggestions" 1241 | }, 1242 | { 1243 | "type": "Question", 1244 | "value": "They found %s in the dumpster behind the abortion clinic.", 1245 | "keep": "Yes", 1246 | "draw": 0, 1247 | "pick": 1, 1248 | "source": "BGG Suggestions" 1249 | }, 1250 | { 1251 | "type": "Question", 1252 | "value": "This decade will be regarded as the golden age of %s.", 1253 | "keep": "Yes", 1254 | "draw": 0, 1255 | "pick": 1, 1256 | "source": "BGG Suggestions" 1257 | }, 1258 | { 1259 | "type": "Question", 1260 | "value": "This Thanksgiving, I'm most thankful for %s.", 1261 | "keep": "Yes", 1262 | "draw": 0, 1263 | "pick": 1, 1264 | "source": "BGG Suggestions" 1265 | }, 1266 | { 1267 | "type": "Question", 1268 | "value": "This year's Pulitzer Prize winning novel was chosen for its brilliant %s, as a metaphor for %s.", 1269 | "keep": "Yes", 1270 | "draw": 1, 1271 | "pick": 2, 1272 | "source": "BGG Suggestions" 1273 | }, 1274 | { 1275 | "type": "Question", 1276 | "value": "Try walking a mile in my %s.", 1277 | "keep": "Yes", 1278 | "draw": 0, 1279 | "pick": 1, 1280 | "source": "BGG Suggestions" 1281 | }, 1282 | { 1283 | "type": "Question", 1284 | "value": "Warning: This product may contain trace amounts of %s.", 1285 | "keep": "Yes", 1286 | "draw": 0, 1287 | "pick": 1, 1288 | "source": "BGG Suggestions" 1289 | }, 1290 | { 1291 | "type": "Question", 1292 | "value": "What two things do you need to survive on a deserted island?", 1293 | "keep": "Yes", 1294 | "draw": 1, 1295 | "pick": 2, 1296 | "source": "BGG Suggestions" 1297 | }, 1298 | { 1299 | "type": "Question", 1300 | "value": "What am I hiding from my parents?", 1301 | "keep": "Yes", 1302 | "draw": 0, 1303 | "pick": 1, 1304 | "source": "BGG Suggestions" 1305 | }, 1306 | { 1307 | "type": "Question", 1308 | "value": "What am I lying to my friends about?", 1309 | "keep": "Yes", 1310 | "draw": 0, 1311 | "pick": 1, 1312 | "source": "BGG Suggestions" 1313 | }, 1314 | { 1315 | "type": "Question", 1316 | "value": "What brought the orgy to a screeching halt?", 1317 | "keep": "Yes", 1318 | "draw": 0, 1319 | "pick": 1, 1320 | "source": "BGG Suggestions" 1321 | }, 1322 | { 1323 | "type": "Question", 1324 | "value": "What did I find in the couch cushions?", 1325 | "keep": "Yes", 1326 | "draw": 0, 1327 | "pick": 1, 1328 | "source": "BGG Suggestions" 1329 | }, 1330 | { 1331 | "type": "Question", 1332 | "value": "What do I fear my parents will find in my sock drawer?", 1333 | "keep": "Yes", 1334 | "draw": 0, 1335 | "pick": 1, 1336 | "source": "BGG Suggestions" 1337 | }, 1338 | { 1339 | "type": "Question", 1340 | "value": "What do I like best about my job?", 1341 | "keep": "Yes", 1342 | "draw": 0, 1343 | "pick": 1, 1344 | "source": "BGG Suggestions" 1345 | }, 1346 | { 1347 | "type": "Question", 1348 | "value": "What do I think about when I'm having trouble getting off?", 1349 | "keep": "Yes", 1350 | "draw": 0, 1351 | "pick": 1, 1352 | "source": "BGG Suggestions" 1353 | }, 1354 | { 1355 | "type": "Question", 1356 | "value": "What gave me AIDS?", 1357 | "keep": "Yes", 1358 | "draw": 0, 1359 | "pick": 1, 1360 | "source": "BGG Suggestions" 1361 | }, 1362 | { 1363 | "type": "Question", 1364 | "value": "What goes best on toast?", 1365 | "keep": "Yes", 1366 | "draw": 0, 1367 | "pick": 1, 1368 | "source": "BGG Suggestions" 1369 | }, 1370 | { 1371 | "type": "Question", 1372 | "value": "What I wouldn't give for 10 minutes alone with %s", 1373 | "keep": "Yes", 1374 | "draw": 0, 1375 | "pick": 1, 1376 | "source": "BGG Suggestions" 1377 | }, 1378 | { 1379 | "type": "Question", 1380 | "value": "What in my past led to my current position on gun control laws?", 1381 | "keep": "Yes", 1382 | "draw": 0, 1383 | "pick": 1, 1384 | "source": "BGG Suggestions" 1385 | }, 1386 | { 1387 | "type": "Question", 1388 | "value": "What is in my zombie apocalypse survival bag?", 1389 | "keep": "Yes", 1390 | "draw": 0, 1391 | "pick": 1, 1392 | "source": "BGG Suggestions" 1393 | }, 1394 | { 1395 | "type": "Question", 1396 | "value": "What is the moral of the story?", 1397 | "keep": "Yes", 1398 | "draw": 0, 1399 | "pick": 1, 1400 | "source": "BGG Suggestions" 1401 | }, 1402 | { 1403 | "type": "Question", 1404 | "value": "What is the only way I can achieve orgasm?", 1405 | "keep": "Yes", 1406 | "draw": 0, 1407 | "pick": 1, 1408 | "source": "BGG Suggestions" 1409 | }, 1410 | { 1411 | "type": "Question", 1412 | "value": "What keeps me up at night?", 1413 | "keep": "Yes", 1414 | "draw": 0, 1415 | "pick": 1, 1416 | "source": "BGG Suggestions" 1417 | }, 1418 | { 1419 | "type": "Question", 1420 | "value": "What makes me tingle all over?", 1421 | "keep": "Yes", 1422 | "draw": 0, 1423 | "pick": 1, 1424 | "source": "BGG Suggestions" 1425 | }, 1426 | { 1427 | "type": "Question", 1428 | "value": "What makes the world go round?", 1429 | "keep": "Yes", 1430 | "draw": 0, 1431 | "pick": 1, 1432 | "source": "BGG Suggestions" 1433 | }, 1434 | { 1435 | "type": "Question", 1436 | "value": "%s melts in your mouth, not in your hand.", 1437 | "keep": "Yes", 1438 | "draw": 0, 1439 | "pick": 1, 1440 | "source": "BGG Suggestions" 1441 | }, 1442 | { 1443 | "type": "Question", 1444 | "value": "What time is it? %s time!", 1445 | "keep": "Yes", 1446 | "draw": 0, 1447 | "pick": 1, 1448 | "source": "BGG Suggestions" 1449 | }, 1450 | { 1451 | "type": "Question", 1452 | "value": "What was Abraham Lincoln's deep, dark secret?", 1453 | "keep": "Yes", 1454 | "draw": 0, 1455 | "pick": 1, 1456 | "source": "BGG Suggestions" 1457 | }, 1458 | { 1459 | "type": "Question", 1460 | "value": "What will be an issue in the next presidential election?", 1461 | "keep": "Yes", 1462 | "draw": 0, 1463 | "pick": 1, 1464 | "source": "BGG Suggestions" 1465 | }, 1466 | { 1467 | "type": "Question", 1468 | "value": "What will be our last chance for salvation when our new robot overlords take over?", 1469 | "keep": "Yes", 1470 | "draw": 0, 1471 | "pick": 1, 1472 | "source": "BGG Suggestions" 1473 | }, 1474 | { 1475 | "type": "Question", 1476 | "value": "What will be put in the time capsule to preserve humanity's memory?", 1477 | "keep": "Yes", 1478 | "draw": 0, 1479 | "pick": 1, 1480 | "source": "BGG Suggestions" 1481 | }, 1482 | { 1483 | "type": "Question", 1484 | "value": "What will I bring to the party?", 1485 | "keep": "Yes", 1486 | "draw": 0, 1487 | "pick": 1, 1488 | "source": "BGG Suggestions" 1489 | }, 1490 | { 1491 | "type": "Question", 1492 | "value": "What's my superpower?", 1493 | "keep": "Yes", 1494 | "draw": 0, 1495 | "pick": 1, 1496 | "source": "BGG Suggestions" 1497 | }, 1498 | { 1499 | "type": "Question", 1500 | "value": "What's the real reason that I've gathered you all hear today?", 1501 | "keep": "Yes", 1502 | "draw": 0, 1503 | "pick": 1, 1504 | "source": "BGG Suggestions" 1505 | }, 1506 | { 1507 | "type": "Question", 1508 | "value": "What's the worst that could happen?", 1509 | "keep": "Yes", 1510 | "draw": 0, 1511 | "pick": 1, 1512 | "source": "BGG Suggestions" 1513 | }, 1514 | { 1515 | "type": "Question", 1516 | "value": "When a man and a woman love each other very much, suddenly %s.", 1517 | "keep": "Yes", 1518 | "draw": 0, 1519 | "pick": 1, 1520 | "source": "BGG Suggestions" 1521 | }, 1522 | { 1523 | "type": "Question", 1524 | "value": "When I get drunk, I feel that I am an expert on %s.", 1525 | "keep": "Yes", 1526 | "draw": 0, 1527 | "pick": 1, 1528 | "source": "BGG Suggestions" 1529 | }, 1530 | { 1531 | "type": "Question", 1532 | "value": "When I get married, our vows will include words about %s.", 1533 | "keep": "Yes", 1534 | "draw": 0, 1535 | "pick": 1, 1536 | "source": "BGG Suggestions" 1537 | }, 1538 | { 1539 | "type": "Question", 1540 | "value": "When I saw your mom naked, it reminded me of %s", 1541 | "keep": "Yes", 1542 | "draw": 0, 1543 | "pick": 1, 1544 | "source": "BGG Suggestions" 1545 | }, 1546 | { 1547 | "type": "Question", 1548 | "value": "When I'm alone and the room is totally dark, I like to secretly %s.", 1549 | "keep": "Yes", 1550 | "draw": 0, 1551 | "pick": 1, 1552 | "source": "BGG Suggestions" 1553 | }, 1554 | { 1555 | "type": "Question", 1556 | "value": "When life gives you %s, make %s.", 1557 | "keep": "Yes", 1558 | "draw": 1, 1559 | "pick": 2, 1560 | "source": "BGG Suggestions" 1561 | }, 1562 | { 1563 | "type": "Question", 1564 | "value": "When you wake up from a night of heavy drinking, you usually regret %s", 1565 | "keep": "Yes", 1566 | "draw": 0, 1567 | "pick": 1, 1568 | "source": "BGG Suggestions" 1569 | }, 1570 | { 1571 | "type": "Question", 1572 | "value": "Where do babies come from?", 1573 | "keep": "Yes", 1574 | "draw": 0, 1575 | "pick": 1, 1576 | "source": "BGG Suggestions" 1577 | }, 1578 | { 1579 | "type": "Question", 1580 | "value": "Who could have guessed that the alien invasion would be easily thwarted by %s.", 1581 | "keep": "Yes", 1582 | "draw": 0, 1583 | "pick": 1, 1584 | "source": "BGG Suggestions" 1585 | }, 1586 | { 1587 | "type": "Question", 1588 | "value": "Who was my childhood hero?", 1589 | "keep": "Yes", 1590 | "draw": 0, 1591 | "pick": 1, 1592 | "source": "BGG Suggestions" 1593 | }, 1594 | { 1595 | "type": "Question", 1596 | "value": "Why am I in such a bad mood?", 1597 | "keep": "Yes", 1598 | "draw": 0, 1599 | "pick": 1, 1600 | "source": "BGG Suggestions" 1601 | }, 1602 | { 1603 | "type": "Question", 1604 | "value": "Why am I like this?", 1605 | "keep": "Yes", 1606 | "draw": 0, 1607 | "pick": 1, 1608 | "source": "BGG Suggestions" 1609 | }, 1610 | { 1611 | "type": "Question", 1612 | "value": "Why are we wearing tuxedos?", 1613 | "keep": "Yes", 1614 | "draw": 0, 1615 | "pick": 1, 1616 | "source": "BGG Suggestions" 1617 | }, 1618 | { 1619 | "type": "Question", 1620 | "value": "Why did the %s have to %s?", 1621 | "keep": "Yes", 1622 | "draw": 1, 1623 | "pick": 2, 1624 | "source": "BGG Suggestions" 1625 | }, 1626 | { 1627 | "type": "Question", 1628 | "value": "Why is mom calling?", 1629 | "keep": "Yes", 1630 | "draw": 0, 1631 | "pick": 1, 1632 | "source": "BGG Suggestions" 1633 | }, 1634 | { 1635 | "type": "Question", 1636 | "value": "Why was my application to work at McDonald's rejected?", 1637 | "keep": "Yes", 1638 | "draw": 0, 1639 | "pick": 1, 1640 | "source": "BGG Suggestions" 1641 | }, 1642 | { 1643 | "type": "Question", 1644 | "value": "With the Democrats and Republicans in a dead heat, the election was stolen by the %s party.", 1645 | "keep": "Yes", 1646 | "draw": 0, 1647 | "pick": 1, 1648 | "source": "BGG Suggestions" 1649 | }, 1650 | { 1651 | "type": "Question", 1652 | "value": "Wonder Twin powers, activate! Shape of %s. Form of %s.", 1653 | "keep": "Yes", 1654 | "draw": 1, 1655 | "pick": 2, 1656 | "source": "BGG Suggestions" 1657 | }, 1658 | { 1659 | "type": "Question", 1660 | "value": "You can never have too much %s.", 1661 | "keep": "Yes", 1662 | "draw": 0, 1663 | "pick": 1, 1664 | "source": "BGG Suggestions" 1665 | }, 1666 | { 1667 | "type": "Question", 1668 | "value": "You know God hates you when %s.", 1669 | "keep": "Yes", 1670 | "draw": 0, 1671 | "pick": 1, 1672 | "source": "BGG Suggestions" 1673 | }, 1674 | { 1675 | "type": "Question", 1676 | "value": "You're in good hands with %s.", 1677 | "keep": "Yes", 1678 | "draw": 0, 1679 | "pick": 1, 1680 | "source": "BGG Suggestions" 1681 | } 1682 | ] 1683 | --------------------------------------------------------------------------------