From 252512fe228406f8c66b7ea80b9d0d5e6926f86c Mon Sep 17 00:00:00 2001 From: Jonn Mostovoy Date: Thu, 17 Mar 2016 13:16:20 +0100 Subject: [PATCH 1/2] Implement Winston draft --- public/src/app.js | 1 + public/src/cards.js | 63 ++++++++++++++++++++++++++++------ public/src/components/game.js | 4 ++- public/src/components/grid.js | 25 ++++++++++++-- public/src/components/lobby.js | 3 +- src/game.js | 62 +++++++++++++++++++++++++++++++-- src/human.js | 12 +++++++ src/util.js | 2 +- 8 files changed, 154 insertions(+), 18 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 12935e6..7ef9be9 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -76,6 +76,7 @@ let App = { ws.on('message', message) }, send(...args) { + console.log('Sending', args) let msg = JSON.stringify(args) this.ws.send(msg) }, diff --git a/public/src/cards.js b/public/src/cards.js index f92c01a..54a9852 100644 --- a/public/src/cards.js +++ b/public/src/cards.js @@ -25,11 +25,37 @@ for (let name in Cards) let rawPack, clicked export let Zones = { pack: {}, + pile: {}, main: {}, side: {}, junk: {} } +function moveCardsToZone(cards, zoneName, dropTo) { + rawPack = cards + if (dropTo !== undefined) { + Zones[zoneName] = dropTo + } + let theZone = Zones[zoneName] + for (let card of cards) { + let {name} = card + Cards[name] = card + theZone[name] || (theZone[name] = 0) + theZone[name]++ + } + App.update() + if (App.state.beep) + document.getElementById('beep').play() +} + +function replaceCardsInZone(cards, zoneName) { + moveCardsToZone(cards, zoneName, {}) +} + +function addCardsToZone(cards, zoneName) { + moveCardsToZone(cards, zoneName) +} + function hash() { let {main, side} = Zones App.send('hash', { main, side }) @@ -70,6 +96,25 @@ let events = { App.update() }, + takePile(zoneName, cards, e) { + let src = Zones[zoneName] + let dst = Zones [e.shiftKey + ? zoneName === 'junk' ? 'main' : 'junk' + : zoneName === 'side' ? 'main' : 'side'] + cards.forEach((card) => { + dst[card.name] || (dst[card.name] = 0) + src[card.name]-- + dst[card.name]++ + if (!src[card.name]) + delete src[card.name] + }) + App.send('takePile', {}) + App.update() + }, + passPile(zoneName, cards, e) { + App.send('passPile', {}) + App.update() + }, copy(ref) { let node = ref.getDOMNode() node.value = filetypes.txt() @@ -88,17 +133,13 @@ let events = { App.send('start', options) }, pack(cards) { - rawPack = cards - let pack = Zones.pack = {} - - for (let card of cards) { - let {name} = card - Cards[name] = card - pack[name] = 1 - } - App.update() - if (App.state.beep) - document.getElementById('beep').play() + replaceCardsInZone(cards, 'pack') + }, + pile(cards) { + replaceCardsInZone(cards, 'pile') + }, + winstonCard(card) { + addCardsToZone([card], 'junk') }, create() { let {type, seats} = App.state diff --git a/public/src/components/game.js b/public/src/components/game.js index b9615e1..ceccd20 100644 --- a/public/src/components/game.js +++ b/public/src/components/game.js @@ -43,9 +43,11 @@ export default React.createClass({ Cards() { if (Object.keys(Zones.pack).length) let pack = Grid({ zones: ['pack'] }) + else + let pile = Grid({ zones: ['pile'] }) let component = App.state.cols ? Cols : Grid let pool = component({ zones: ['main', 'side', 'junk'] }) - return [pack, pool] + return [pack, pile, pool] }, Start() { if (App.state.round || !App.state.isHost) diff --git a/public/src/components/grid.js b/public/src/components/grid.js index 4a47ceb..34048b4 100644 --- a/public/src/components/grid.js +++ b/public/src/components/grid.js @@ -14,15 +14,36 @@ function zone(zoneName) { let zone = getZone(zoneName) let values = _.values(zone) let cards = _.flat(values) + let perPileEmission = zoneName === 'pile' + + let emitMaybe = (card) => { + if (! perPileEmission) + return App._emit('click', zoneName, card.name) + return () => {} + } + + let pileControlsMaybe = () => { + if (perPileEmission) { + return d.div({}, + d.button({ + onClick: App._emit('passPile', zoneName, cards) + }, 'pass pile'), + d.button({ + onClick: App._emit('takePile', zoneName, cards) + }, 'take pile') + ) + } + return d.span({}) + } let items = cards.map(card => d.img({ - onClick: App._emit('click', zoneName, card.name), + onClick: emitMaybe(card), src: card.url, alt: card.name })) return d.div({ className: 'zone' }, d.h1({}, `${zoneName} ${cards.length}`), - items) + [pileControlsMaybe(), items]) } diff --git a/public/src/components/lobby.js b/public/src/components/lobby.js index 1aededb..36503f1 100644 --- a/public/src/components/lobby.js +++ b/public/src/components/lobby.js @@ -66,6 +66,7 @@ function content() { case 'sealed': return [setsTop, setsBot] case 'cube draft' : return [cube, cubeDraft] case 'cube sealed': return cube + case 'cube winston': return [cube, cubeDraft] case 'editor': return d.a({ href: 'http://editor.draft.wtf' }, 'editor') } } @@ -74,7 +75,7 @@ function Create() { let seats = _.seq(8, 2).map(x => d.option({}, x)) - let types = ['draft', 'sealed', 'cube draft', 'cube sealed', 'editor'] + let types = ['draft', 'sealed', 'cube draft', 'cube sealed', 'cube winston', 'editor'] .map(type => d.button({ disabled: type === App.state.type, diff --git a/src/game.js b/src/game.js index ef002cb..6eb675c 100644 --- a/src/game.js +++ b/src/game.js @@ -40,7 +40,7 @@ module.exports = class Game extends Room { title: sets.join(' / ')}) else { var title = type - if (type === 'cube draft') + if (type === 'cube draft' || type === 'cube winston') title += ' ' + cube.packs + 'x' + cube.cards Object.assign(this, { cube, title }) } @@ -52,7 +52,8 @@ module.exports = class Game extends Room { id: gameID, players: [], round: 0, - rounds: cube ? cube.packs : 3 + rounds: cube ? cube.packs : 3, + winston: (type === 'cube winston') }) this.renew() games[gameID] = this @@ -173,6 +174,34 @@ module.exports = class Game extends Room { this.meta() } + bumpAp() { + return (this.ap + 1) % this.players.length + } + + takePile(p) { + if (p.id !== this.players[this.ap].id) + return + + this.piles[this.pile].forEach((x) => { this.players[this.ap].pool.push(x) }) + this.piles[this.pile] = [this.pool.pop()] + this.players[this.ap].sendPile([]) + + this.ap = this.bumpAp() + this.pile = 0 + + this.startWinstonPileSelection() + } + + passPile(p) { + if (p.id !== this.players[this.ap].id) + return + + this.piles[this.pile].push(this.pool.pop()) + this.pile++ + + this.startWinstonPileSelection() + } + startRound() { if (this.round++ === this.rounds) return this.end() @@ -199,6 +228,21 @@ module.exports = class Game extends Room { this.meta({ round: this.round }) } + startWinstonPileSelection() { + if ( this.pile >= this.piles.length ) { + let topCard = this.pool.pop() + this.players[this.ap].pool.push(topCard) + this.players[this.ap].sendCard(topCard) + this.players[this.ap].sendPile([]) + this.ap = this.bumpAp() + this.pile = 0 + this.startWinstonPileSelection() + } + else { + this.players[this.ap].sendPile(this.piles[this.pile]) + } + } + hash(h, deck) { h.hash = hash(deck) this.meta() @@ -222,6 +266,20 @@ module.exports = class Game extends Room { return } + if (/winston/.test(this.type)) { + this.pile = 0 + this.ap = 0 + var [pool] = Pool(src, 1, true) + this.pool = pool + this.piles = [[this.pool.pop()], [this.pool.pop()], [this.pool.pop()]] + players.forEach((p, i) => { + p.on('takePile', this.takePile.bind(this, p)) + p.on('passPile', this.passPile.bind(this, p)) + p.send('set', { self: i }) + }) + return this.startWinstonPileSelection() + } + for (p of players) p.useTimer = useTimer diff --git a/src/human.js b/src/human.js index 510d1f6..2f6bee2 100644 --- a/src/human.js +++ b/src/human.js @@ -10,6 +10,7 @@ module.exports = class extends EventEmitter { name: sock.name, time: 0, packs: [], + pile: [], pool: [] }) this.attach(sock) @@ -21,6 +22,8 @@ module.exports = class extends EventEmitter { sock.mixin(this) sock.on('pick', this._pick.bind(this)) sock.on('hash', this._hash.bind(this)) + sock.on('takePile', (() => this.emit('takePile')).bind(this)) + sock.on('passPile', (() => this.emit('passPile')).bind(this)) var [pack] = this.packs if (pack) @@ -52,6 +55,15 @@ module.exports = class extends EventEmitter { this.send('pack', pack) } + sendCard(card) { + this.send('winstonCard', card) + } + sendPile(pile) { + if (this.useTimer) + this.time = 20 + 5 * pile.length + + this.send('pile', pile) + } pick(index) { var pack = this.packs.shift() var card = pack.splice(index, 1)[0] diff --git a/src/util.js b/src/util.js index 7cfbd20..2cf66bf 100644 --- a/src/util.js +++ b/src/util.js @@ -65,7 +65,7 @@ var util = module.exports = { game({seats, type, sets, cube}) { assert(typeof seats === 'number', 'typeof seats') assert(2 <= seats && seats <= 8, 'seats range') - assert(['draft', 'sealed', 'cube draft', 'cube sealed'].indexOf(type) > -1, + assert(['draft', 'sealed', 'cube draft', 'cube winston', 'cube sealed'].indexOf(type) > -1, 'indexOf type') if (/cube/.test(type)) From a4301a3a293df36e642ed40e3aa17b4ee463da55 Mon Sep 17 00:00:00 2001 From: Jonn Mostovoy Date: Fri, 29 Apr 2016 19:08:09 +0300 Subject: [PATCH 2/2] Add SOI, tiny refactoring, increase max cube cards --- public/src/data.js | 1 + src/_.js | 2 +- src/make/cards.js | 50 ++++++++++++++++++ src/pool.js | 127 +++++++++++++++++++++++++++------------------ src/util.js | 4 +- 5 files changed, 131 insertions(+), 53 deletions(-) diff --git a/public/src/data.js b/public/src/data.js index 7031dbc..d3afd65 100644 --- a/public/src/data.js +++ b/public/src/data.js @@ -1,5 +1,6 @@ export default { expansion: { + "Shadows Over Innistrad": "SOI", "Oath of the Gatewatch": "OGW", "Battle for Zendikar": "BFZ", "Dragons of Tarkir": "DTK", diff --git a/src/_.js b/src/_.js index ae15ebf..23fb91b 100644 --- a/src/_.js +++ b/src/_.js @@ -14,7 +14,7 @@ module.exports = { }, at(arr, index) { var {length} = arr - index = (index % length + length) % length//please kill me it hurts to live + index = (index % length + length) % length return arr[index] }, count(arr, attr) { diff --git a/src/make/cards.js b/src/make/cards.js index 300b9cd..384cab8 100644 --- a/src/make/cards.js +++ b/src/make/cards.js @@ -62,6 +62,12 @@ function before() { || /draft/.test(card.text)) card.rarity = 'special' + for (card of raw.SOI.cards) { + if (card.layout === 'double-faced') { + card.rarity = 'special' + } + } + for (card of raw.FRF.cards) if (card.types[0] === 'Land' && (card.name !== 'Crucible of the Spirit Dragon')) @@ -130,6 +136,50 @@ function after() { 'scorned villager' ] } + var {SOI} = Sets + SOI.special = { + mythic: [ + 'archangel avacyn', + 'startled awake', + 'arlinn kord' + ], + rare: [ + 'hanweir militia captain', + 'thing in the ice', + 'elusive tormentor', + 'geier reach bandit', + 'sage of ancient lore' + ], + uncommon: [ + 'avacynian missionaries', + 'pious evangel', + 'town gossipmonger', + 'aberrant researcher', + 'daring sleuth', + 'uninvited geist', + 'accursed witch', + 'heir of falkenrath', + 'kindly stranger', + 'breakneck rider', + 'kessig forgemaster', + 'skin invasion', + 'village messenger', + 'autumnal gloom', + 'cult of the waxing moon', + 'duskwatch recruiter', + 'hermit of the natterknolls', + 'lambholt pacifist', + 'harvest hand', + 'neglected heirloom', + 'thraben gargoyle' + ], + common: [ + 'convicted killer', + 'gatstaf arsonists', + 'hinterland logger', + 'solitary hunter' + ] + } var {DGM} = Sets DGM.mythic.splice(DGM.mythic.indexOf("maze's end"), 1) DGM.special = { diff --git a/src/pool.js b/src/pool.js index f5c1834..ac2b675 100644 --- a/src/pool.js +++ b/src/pool.js @@ -32,58 +32,85 @@ function toPack(code) { _.choose(1, rare) ) - switch (code) { - case 'DGM': - special = _.rand(20) - ? special.gate - : special.shock - break - case 'MMA': - special = selectRarity(set) - break - case 'MM2': - special = selectRarity(set) - break - case 'VMA': - //http://www.wizards.com/magic/magazine/article.aspx?x=mtg/daily/arcana/1491 - if (_.rand(53)) - special = selectRarity(set) - break - case 'FRF': - special = _.rand(20) - ? special.common - : special.fetch - break - case 'ISD': - //http://www.mtgsalvation.com/forums/magic-fundamentals/magic-general/327956-innistrad-block-transforming-card-pack-odds?comment=4 - //121 card sheet, 1 mythic, 12 rare (13), 42 uncommon (55), 66 common - specialrnd = _.rand(121) - if (specialrnd == 0) - special = special.mythic - else if (specialrnd < 13) - special = special.rare - else if (specialrnd < 55) - special = special.uncommon - else - special = special.common - break - case 'DKA': - //http://www.mtgsalvation.com/forums/magic-fundamentals/magic-general/327956-innistrad-block-transforming-card-pack-odds?comment=4 - //80 card sheet, 2 mythic, 6 rare (8), 24 uncommon (32), 48 common - specialrnd = _.rand(80) - if (specialrnd <= 1) - special = special.mythic - else if (specialrnd < 8) - special = special.rare - else if (specialrnd < 32) - special = special.uncommon - else - special = special.common - break + var special1 + var specialrnd + switch (code) { // the author of this switch should be whipped with a switch + // I have fixed it after quite some time of debugging while + // adding SOI. MUTABILITY IS YOUR ENEMY. + // ~~ @manpages + + case 'DGM': + special1 = _.rand(20) + ? special.gate + : special.shock + break + + case 'MMA': + special1 = selectRarity(set) + break + + case 'MM2': + special1 = selectRarity(set) + break + + case 'VMA': + //http://www.wizards.com/magic/magazine/article.aspx?x=mtg/daily/arcana/1491 + if (_.rand(53)) + special1 = selectRarity(set) + break + + case 'FRF': + special1 = _.rand(20) + ? special.common + : special.fetch + break + + case 'ISD': + //http://www.mtgsalvation.com/forums/magic-fundamentals/magic-general/327956-innistrad-block-transforming-card-pack-odds?comment=4 + //121 card sheet, 1 mythic, 12 rare (13), 42 uncommon (55), 66 common + specialrnd = _.rand(121) + if (specialrnd == 0) + special1 = special.mythic + else if (specialrnd < 13) + special1 = special.rare + else if (specialrnd < 55) + special1 = special.uncommon + else + special1 = special.common + break + + case 'SOI': + // Copied ISD + //121 card sheet, 1 mythic, 12 rare (13), 42 uncommon (55), 66 common + specialrnd = _.rand(121) + if (specialrnd == 0) + special1 = special.mythic + else if (specialrnd < 13) + special1 = special.rare + else if (specialrnd < 55) + special1 = special.uncommon + else + special1 = special.common + break + + case 'DKA': + //http://www.mtgsalvation.com/forums/magic-fundamentals/magic-general/327956-innistrad-block-transforming-card-pack-odds?comment=4 + //80 card sheet, 2 mythic, 6 rare (8), 24 uncommon (32), 48 common + specialrnd = _.rand(80) + if (specialrnd <= 1) + special1 = special.mythic + else if (specialrnd < 8) + special1 = special.rare + else if (specialrnd < 32) + special1 = special.uncommon + else + special1 = special.common + break + } - if (special) - pack.push(_.choose(1, special)) + if (special1) + pack.push(_.choose(1, special1)) return toCards(pack, code) } diff --git a/src/util.js b/src/util.js index 2cf66bf..f7f8f3b 100644 --- a/src/util.js +++ b/src/util.js @@ -23,8 +23,8 @@ function transform(cube, seats, type) { var min = type === 'cube draft' ? seats * cards * packs : seats * 90 - assert(min <= list.length && list.length <= 1e3, - `this cube needs between ${min} and 1000 cards; it has ${list.length}`) + assert(min <= list.length && list.length <= 9e3, + `this cube needs between ${min} and 9000 cards; it has ${list.length}`) var bad = [] for (var cardName of list)