Animate turns by showing the cards movements

This commit is contained in:
Tissevert 2019-01-13 20:01:55 +01:00
parent 2a9b162324
commit 6b26ce72a1
5 changed files with 202 additions and 94 deletions

View file

@ -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;
}
}

View file

@ -96,6 +96,17 @@
background-position-y: 91.7%; 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 { #game #rest {
margin: 0; margin: 0;
} }

View file

@ -7,58 +7,181 @@ function Game(modules) {
step: null, step: null,
month: null month: null
}; };
var sets = { var sets = buildSets();
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 selected = null; var selected = null;
var queue = [];
window.addEventListener('focus', runQueue);
modules.messaging.addEventListener(["Game"], function(o) { modules.messaging.addEventListener(["Game"], function(o) {
setStatus(o.game); if(document.hasFocus()) {
setCaptures(o.game); modules.async.run(handleGameMessage(o));
[ } else {
['river', o.game.river, RiverCard], queue.push(handleGameMessage(o));
['yourHand', o.game.players[modules.session.getKey()].hand, HandCard] }
].forEach(function(args) {setCardSet.apply(null, args)}); });
setTheirCards(o.game);
if(status.step == "Turned") { function buildSets() {
setTurned(o.game.step.contents); var sets = {};
} else { ['river', 'you', 'them'].forEach(function(id) {
if(status.step == "ToPlay" && o.game.playing == o.game.oyake) { var dom = document.getElementById(id);
rest.className = ["card", "count" + o.game.deck].join(' '); if(dom.tagName.toLowerCase() == 'ul') {
} sets[id] = {card: null, dom: dom};
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});}}
]
});
} else { } else {
modules.screen.dialog({ sets[id] = {};
text: modules.i18n.get('theyScored')(modules.room.name(o.game.playing)), for(var i = 0; i < dom.children.length; i++) {
answers: [ if(dom.children[i].tagName.toLowerCase() == 'ul') {
{label: 'ok', action: function() {}} 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) { function play(move) {
modules.messaging.send({ modules.messaging.send({
@ -86,10 +209,10 @@ function Game(modules) {
var turn = null; var turn = null;
status.playing = modules.session.is(game.playing); status.playing = modules.session.is(game.playing);
if(status.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"); turn = modules.i18n.get("yourTurn");
} else { } else {
sets.yourHand.dom.classList.remove("yourTurn"); sets.you.hand.dom.classList.remove("yourTurn");
turn = modules.i18n.get('playing')(modules.room.name(game.playing)); turn = modules.i18n.get('playing')(modules.room.name(game.playing));
} }
status.dom.appendChild(modules.dom.make('li', {textContent: turn})); 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; constructor = constructor || Card;
var set = sets[setName];
set.card = {}; set.card = {};
modules.dom.clear(set.dom); modules.dom.clear(set.dom);
cardNames.forEach(function(cardName) { cardNames.forEach(function(cardName) {
@ -127,16 +249,21 @@ function Game(modules) {
var turnsTheyPlayed = Math.floor( var turnsTheyPlayed = Math.floor(
(24 - game.deck + (modules.session.is(game.oyake) ? 0 : 1)) / 2 (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++) { 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); var card = new Card(cardName);
card.dom.id = "turned"; card.dom.id = "turned";
deck.appendChild(card.dom); deck.appendChild(card.dom);
return card;
}
function setTurned(cardName) {
turnedCard(cardName);
if(status.playing) { if(status.playing) {
selected = cardName; selected = cardName;
showCandidates(modules.hanafuda.Card[selected], true); showCandidates(modules.hanafuda.Card[selected], true);
@ -173,6 +300,7 @@ function Game(modules) {
if(card.candidate) { if(card.candidate) {
var withCard = selected; var withCard = selected;
selected = null; selected = null;
showCandidates(card.value, false);
play( play(
status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name} status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name}
); );
@ -190,18 +318,16 @@ function Game(modules) {
} }
HandCard.prototype.onClick = function() { HandCard.prototype.onClick = function() {
if(status.playing && status.step == "ToPlay") { var card = this;
var card = this; return function() {
return function() { if(status.playing && status.step == "ToPlay") {
if(selected != undefined) { if(selected != undefined) {
sets.yourHand.card[selected].setSelected(false); sets.you.hand.card[selected].setSelected(false);
} else { } else {
card.play(); card.play();
} }
}; }
} else { };
return function() {};
}
}; };
HandCard.prototype.setSelected = function(yes) { HandCard.prototype.setSelected = function(yes) {

View file

@ -3,7 +3,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>KoiKoi</title> <title>KoiKoi</title>
<script src="dom.js"></script> <script src="UnitJS/async.js"></script>
<script src="UnitJS/dom.js"></script>
<script src="translations.js"></script> <script src="translations.js"></script>
<script src="i18n.js"></script> <script src="i18n.js"></script>
<script src="fun.js"></script> <script src="fun.js"></script>

View file

@ -1,5 +1,6 @@
window.addEventListener('load', function() { window.addEventListener('load', function() {
var dom = Dom(); var dom = Dom();
var async = Async();
var translations = Translations(); var translations = Translations();
var i18n = I18n({translations: translations}); var i18n = I18n({translations: translations});
var fun = Fun(); var fun = Fun();
@ -9,7 +10,7 @@ window.addEventListener('load', function() {
var room = Room({dom: dom, messaging: messaging, session: session, fun: fun}); 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 login = Login({dom: dom, i18n: i18n, messaging: messaging, room: room, screen: screen, session: session});
var hanafuda = Hanafuda({fun: fun}); 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 = { var domElems = {
join: document.getElementById('login').join, join: document.getElementById('login').join,