From 6b26ce72a11f593138b1d85f406f987b53f1af16 Mon Sep 17 00:00:00 2001 From: Tissevert Date: Sun, 13 Jan 2019 20:01:55 +0100 Subject: [PATCH] Animate turns by showing the cards movements --- www/dom.js | 31 ------- www/game.css | 11 +++ www/game.js | 248 +++++++++++++++++++++++++++++++++++++------------ www/index.html | 3 +- www/main.js | 3 +- 5 files changed, 202 insertions(+), 94 deletions(-) delete mode 100644 www/dom.js diff --git a/www/dom.js b/www/dom.js deleted file mode 100644 index 997730e..0000000 --- a/www/dom.js +++ /dev/null @@ -1,31 +0,0 @@ -function Dom() { - return { - clear: clear, - make: make - } - - function clear(elem) { - while(elem.firstChild) { - elem.removeChild(elem.firstChild); - } - } - - function make(tag, properties) { - var e = document.createElement(tag); - for(key in properties) { - var value = properties[key]; - switch(key) { - case "class": - e.className = Array.isArray(value) ? value.join(' ') : value; - break;; - case "onClick": - e.addEventListener("click", value); - break;; - default: - e[key] = value; - } - } - return e; - } - -} diff --git a/www/game.css b/www/game.css index 335c72e..1ccb1c9 100644 --- a/www/game.css +++ b/www/game.css @@ -96,6 +96,17 @@ background-position-y: 91.7%; } +#game .card.slot { + background: none; + border: 1px solid transparent; +} + +#game .card.moving { + position: relative; + transition-property: left, top; + transition-duration: 1s; +} + #game #rest { margin: 0; } diff --git a/www/game.js b/www/game.js index aea89ba..f9898f3 100644 --- a/www/game.js +++ b/www/game.js @@ -7,58 +7,181 @@ function Game(modules) { step: null, month: null }; - var sets = { - river: { - card: null, - dom: document.getElementById("river") - }, - yourHand: { - card: null, - dom: document.getElementById("you").getElementsByClassName("hand")[0] - }, - theirHand: { - dom: document.getElementById("them").getElementsByClassName("hand")[0] - } - }; + var sets = buildSets(); var selected = null; + var queue = []; + window.addEventListener('focus', runQueue); modules.messaging.addEventListener(["Game"], function(o) { - setStatus(o.game); - setCaptures(o.game); - [ - ['river', o.game.river, RiverCard], - ['yourHand', o.game.players[modules.session.getKey()].hand, HandCard] - ].forEach(function(args) {setCardSet.apply(null, args)}); - setTheirCards(o.game); - if(status.step == "Turned") { - setTurned(o.game.step.contents); - } else { - if(status.step == "ToPlay" && o.game.playing == o.game.oyake) { - rest.className = ["card", "count" + o.game.deck].join(' '); - } - if(deck.lastChild.id != "rest") { - deck.removeChild(deck.lastChild); - } - } - if(status.step == "Scored") { - if(status.playing) { - modules.screen.dialog({ - text: modules.i18n.get('youScored'), - answers: [ - {label: 'endRound', action: function() {play({koiKoi: false})}}, - {label: 'koikoi', action: function() {play({koiKoi: true});}} - ] - }); + if(document.hasFocus()) { + modules.async.run(handleGameMessage(o)); + } else { + queue.push(handleGameMessage(o)); + } + }); + + function buildSets() { + var sets = {}; + ['river', 'you', 'them'].forEach(function(id) { + var dom = document.getElementById(id); + if(dom.tagName.toLowerCase() == 'ul') { + sets[id] = {card: null, dom: dom}; } else { - modules.screen.dialog({ - text: modules.i18n.get('theyScored')(modules.room.name(o.game.playing)), - answers: [ - {label: 'ok', action: function() {}} - ] - }); + sets[id] = {}; + for(var i = 0; i < dom.children.length; i++) { + if(dom.children[i].tagName.toLowerCase() == 'ul') { + sets[id][dom.children[i].className] = {card: {}, dom: dom.children[i]}; + } + } } + }); + return sets; + } + + function runQueue() { + if(queue.length > 0) { + modules.async.run.apply(null, queue.concat( + modules.async.apply(function() {queue = [];}) + )); + } + } + + function handleGameMessage(o) { + if(o.game.deck == 24) { + return o.logs.length > 0 ? modules.async.sequence(applyDiff(o), setGame(o)) : setGame(o); + } else { + return applyDiff(o); + } + } + + function setGame(o) { + return function(f) { + setStatus(o.game); + setCaptures(o.game); + [ + [sets.river, o.game.river, RiverCard], + [sets.you.hand, o.game.players[modules.session.getKey()].hand, HandCard] + ].forEach(function(args) {setCardSet.apply(null, args)}); + setTheirCards(o.game); + handleStep(o)(f); + }; + } + + function handleStep(o) { + return function(f) { + if(status.step == "Turned") { + setTurned(o.game.step.contents); + } else { + if(status.step == "ToPlay" && o.game.playing == o.game.oyake) { + rest.className = ["card", "count" + o.game.deck].join(' '); + } + if(deck.lastChild.id != "rest") { + deck.removeChild(deck.lastChild); + } + } + if(status.step == "Scored") { + if(status.playing) { + modules.screen.dialog({ + text: modules.i18n.get('youScored'), + answers: [ + {label: 'endRound', action: function() {play({koiKoi: false}); f();}}, + {label: 'koikoi', action: function() {play({koiKoi: true}); f();}} + ] + }); + } else { + modules.screen.dialog({ + text: modules.i18n.get('theyScored')(modules.room.name(o.game.playing)), + answers: [ + {label: 'ok', action: f} + ] + }); + } + } else { + f(); + } + }; + } + + function applyDiff(o) { + return modules.async.sequence.apply(null, + o.logs.map(animate).concat( + modules.async.apply(setStatus, o.game), + handleStep(o) + ) + ); + } + + function animate(movement) { + return modules.async.bind( + modules.async.apply(function() { + var card; + var movingCards = []; + var side = (status.playing) ? 'you' : 'them'; + var dest = sets.river; + if(movement.captures != undefined) { + card = new Card(movement.played); + dest = sets[side]; + movingCards.push([sets.river, dest, new Card(movement.captures)]); + } else { + card = new RiverCard(movement.played); + } + if(movement.source == 'Hand') { + movingCards.push([sets[side].hand, dest, card]); + } else { + var cardSet = {}; + cardSet[card.name] = turnedCard(card.name); + movingCards.push([{card: cardSet, dom: deck}, dest, card]); + } + return movingCards; + }), + function(movingCards) { + return modules.async.parallel.apply(null, + movingCards.map(function(args) { return moveCard.apply(null, args); }) + ); + } + ); + } + + function moveCard(fromSet, toSet, card) { + var from, originalCard; + var slot = modules.dom.make('li', {class: ['card', 'slot']}); + if (fromSet.card[card.name] != undefined) { + originalCard = fromSet.card[card.name].dom; + delete fromSet.card[card.name]; + } else { + var originalCard = fromSet.dom.children[fromSet.dom.children.length - 1]; } - }); + from = originalCard.getBoundingClientRect(); + fromSet.dom.replaceChild(slot, originalCard); + card.dom.style.visibility = 'hidden'; + insertCard(toSet, card); + var to = card.dom.getBoundingClientRect(); + card.dom.style.left = (from.left - to.left) + 'px'; + card.dom.style.top = (from.top - to.top) + 'px'; + card.dom.classList.add('moving'); + card.dom.style.visibility = null; + return modules.async.sequence( + modules.async.wait(10), + modules.async.apply(function() { + card.dom.style.left = 0; + card.dom.style.top = 0; + }), + modules.async.wait(1000), + modules.async.apply(function() { + fromSet.dom.removeChild(slot); + card.dom.classList.remove('moving'); + }) + ); + } + + function insertCard(toSet, card) { + if(toSet.dom != undefined) { + toSet.card[card.name] = card; + toSet.dom.appendChild(card.dom); + } else { + insertCard(toSet[card.value.family.class], card); + } + } function play(move) { modules.messaging.send({ @@ -86,10 +209,10 @@ function Game(modules) { var turn = null; status.playing = modules.session.is(game.playing); if(status.playing) { - sets.yourHand.dom.classList.toggle("yourTurn", status.step == "ToPlay"); + sets.you.hand.dom.classList.toggle("yourTurn", status.step == "ToPlay"); turn = modules.i18n.get("yourTurn"); } else { - sets.yourHand.dom.classList.remove("yourTurn"); + sets.you.hand.dom.classList.remove("yourTurn"); turn = modules.i18n.get('playing')(modules.room.name(game.playing)); } status.dom.appendChild(modules.dom.make('li', {textContent: turn})); @@ -111,9 +234,8 @@ function Game(modules) { } } - function setCardSet(setName, cardNames, constructor) { + function setCardSet(set, cardNames, constructor) { constructor = constructor || Card; - var set = sets[setName]; set.card = {}; modules.dom.clear(set.dom); cardNames.forEach(function(cardName) { @@ -127,16 +249,21 @@ function Game(modules) { var turnsTheyPlayed = Math.floor( (24 - game.deck + (modules.session.is(game.oyake) ? 0 : 1)) / 2 ); - modules.dom.clear(sets.theirHand.dom); + modules.dom.clear(sets.them.hand.dom); for(var i = 0; i < 8 - turnsTheyPlayed; i++) { - sets.theirHand.dom.appendChild(modules.dom.make('li', {class: "card"})); + sets.them.hand.dom.appendChild(modules.dom.make('li', {class: "card"})); } } - function setTurned(cardName) { + function turnedCard(cardName) { var card = new Card(cardName); card.dom.id = "turned"; deck.appendChild(card.dom); + return card; + } + + function setTurned(cardName) { + turnedCard(cardName); if(status.playing) { selected = cardName; showCandidates(modules.hanafuda.Card[selected], true); @@ -173,6 +300,7 @@ function Game(modules) { if(card.candidate) { var withCard = selected; selected = null; + showCandidates(card.value, false); play( status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name} ); @@ -190,18 +318,16 @@ function Game(modules) { } HandCard.prototype.onClick = function() { - if(status.playing && status.step == "ToPlay") { - var card = this; - return function() { + var card = this; + return function() { + if(status.playing && status.step == "ToPlay") { if(selected != undefined) { - sets.yourHand.card[selected].setSelected(false); + sets.you.hand.card[selected].setSelected(false); } else { card.play(); } - }; - } else { - return function() {}; - } + } + }; }; HandCard.prototype.setSelected = function(yes) { diff --git a/www/index.html b/www/index.html index b54e639..439b986 100644 --- a/www/index.html +++ b/www/index.html @@ -3,7 +3,8 @@ KoiKoi - + + diff --git a/www/main.js b/www/main.js index 323f5e7..04ffd1d 100644 --- a/www/main.js +++ b/www/main.js @@ -1,5 +1,6 @@ window.addEventListener('load', function() { var dom = Dom(); + var async = Async(); var translations = Translations(); var i18n = I18n({translations: translations}); var fun = Fun(); @@ -9,7 +10,7 @@ window.addEventListener('load', function() { var room = Room({dom: dom, messaging: messaging, session: session, fun: fun}); var login = Login({dom: dom, i18n: i18n, messaging: messaging, room: room, screen: screen, session: session}); var hanafuda = Hanafuda({fun: fun}); - var game = Game({dom: dom, i18n: i18n, fun: fun, hanafuda: hanafuda, messaging: messaging, room: room, screen: screen, session: session}); + var game = Game({async: async, dom: dom, i18n: i18n, fun: fun, hanafuda: hanafuda, messaging: messaging, room: room, screen: screen, session: session}); var domElems = { join: document.getElementById('login').join,