diff --git a/js/GUI/Card.js b/js/GUI/Card.js
index 82cdd11..25b3a83 100644
--- a/js/GUI/Card.js
+++ b/js/GUI/Card.js
@@ -1,21 +1,16 @@
import Hanafuda;
import * as Dom from UnitJS.Dom;
-function Card(name) {
- var value = Hanafuda.Card[name];
- var dom = Dom.make('li', {
- class: [
- "card",
- "value" + Hanafuda.getValue(this.value),
- "month" + this.value.flower
- ]
- });
- return {
- value: value,
- dom: dom
- };
-}
-
return {
make: Card
};
+
+function Card(name) {
+ var value = Hanafuda.Card[name];
+ return {
+ value: value,
+ dom: Dom.make('li', {
+ class: ["card", "value" + value.monthOffset, "month" + value.flower]
+ })
+ };
+}
diff --git a/js/GUI/Card/HandCard.js b/js/GUI/Card/HandCard.js
index d7043e2..b7e1deb 100644
--- a/js/GUI/Card/HandCard.js
+++ b/js/GUI/Card/HandCard.js
@@ -1,28 +1,34 @@
+import * as Card from GUI.Card;
+import * as State from GUI.Screen.Game.State;
-function HandCard() {
- Card.apply(this, arguments);
-}
-
-HandCard.prototype.onClick = function() {
- var card = this;
- return function() {
- if(status.playing && status.step == "ToPlay") {
- if(selected != undefined) {
- selected.setSelected(false);
- } else {
- card.play();
- }
- }
- };
+return {
+ make: HandCard
};
-HandCard.prototype.setSelected = setSelected;
+function HandCard(name) {
+ var card = Card.make(name);
+ card.onClick = onClick;
+ return card;
-HandCard.prototype.play = function() {
- var matching = matchingInRiver(this.value);
- if(matching.length > 1) {
- this.setSelected(true);
- } else {
- play({play: this.name});
- }
+ function onClick() {
+ return function() {
+ if(State.state.playing && State.state.step == "ToPlay") {
+ if(State.getSelected() != undefined) {
+ State.select(false);
+ } else {
+ play();
+ }
+ }
+ };
+ }
+
+ function play() {
+ var matching = State.matchingInRiver(card.value);
+ if(matching.length > 1) {
+ State.select(card);
+ } else {
+ State.play({play: card.name});
+ }
+ }
}
+
diff --git a/js/GUI/Card/RiverCard.js b/js/GUI/Card/RiverCard.js
index ba51be1..ccaa48d 100644
--- a/js/GUI/Card/RiverCard.js
+++ b/js/GUI/Card/RiverCard.js
@@ -1,4 +1,9 @@
import * as Card from GUI.Card;
+import * as State from GUI.Screen.Game.State;
+
+return {
+ make: RiverCard
+};
function RiverCard(name) {
var card = Card.make(name);
@@ -10,25 +15,18 @@ function RiverCard(name) {
function onClick() {
return function() {
if(candidate) {
- var withCard = selected.name;
- selected.setSelected(false);
- play(
- status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name}
+ var withCard = State.getSelected().name;
+ State.select(false);
+ State.play(
+ State.state.step == 'ToPlay' ?
+ {capture: [withCard, card.name]} : {choose: card.name}
);
}
};
}
- function setCandidate() {
+ function setCandidate(yes) {
candidate = yes;
card.dom.classList.toggle("candidate", yes);
}
}
-
-RiverCard.prototype.onClick = function() {
- var card = this;
-};
-
-return {
- make: RiverCard
-};
diff --git a/js/GUI/Card/TurnedCard.js b/js/GUI/Card/TurnedCard.js
index dbf79f3..e4882a9 100644
--- a/js/GUI/Card/TurnedCard.js
+++ b/js/GUI/Card/TurnedCard.js
@@ -1,14 +1,13 @@
import * as Card from GUI.Card;
-
-function TurnedCard(name) {
- var card = Card.make(name);
- card.dom.id = "turned";
- deck.appendChild(this.dom);
-}
-
-TurnedCard.prototype.onClick = Card.prototype.onClick;
-TurnedCard.prototype.setSelected = setSelected;
+import dom from GUI.Screen.Game.State;
return {
make: TurnedCard
};
+
+function TurnedCard(name) {
+ var card = Card.make(name);
+ card.dom.id = "turned";
+ dom.deck.appendChild(card.dom);
+ return card;
+}
diff --git a/js/GUI/Screen/Game.js b/js/GUI/Screen/Game.js
index c381782..0e6fb2f 100644
--- a/js/GUI/Screen/Game.js
+++ b/js/GUI/Screen/Game.js
@@ -1,74 +1,77 @@
+import * as Card from GUI.Card;
+import * as HandCard from GUI.Card.HandCard;
+import * as RiverCard from GUI.Card.RiverCard;
+import * as TurnedCard from GUI.Card.TurnedCard;
+import * as Screen from GUI.Screen;
+import {animate, catchUp, delay, getQueue} from GUI.Screen.Game.Animation;
+import {dom, init as initState, select, sets, state} from GUI.Screen.Game.State;
import Hanafuda;
import I18n;
import Messaging;
import players from Room;
-import * as Screen from GUI.Screen;
+import Save;
import Session;
import StatusHandler;
import * as Async from UnitJS.Async;
import * as Dom from UnitJS.Dom;
import * as Fun from UnitJS.Fun;
-var deck = document.getElementById("deck");
-var rest = document.getElementById("rest");
-var status = {
- dom: document.getElementById("status"),
- game: null,
- playing: false,
- step: null,
- month: null
-};
-var sets;
-var selected = null;
-var turnedCard = null;
-var queue = [];
-
return {
init: init
}
-function init(state) {
- sets = buildSets();
- window.addEventListener('focus', runQueue);
+function init(gameID) {
+ initState();
+ initMessageHandlers();
+ Async.run(
+ Async.bind(
+ getSavedState(gameID),
+ function(state) {
+ return Async.sequence(
+ startSession(),
+ setGame({tag: "Game", state: state, logs: []})
+ );
+ }
+ )
+ );
+}
+
+function fail(errorCode) {
+ return function(f) {
+ Screen.dialog({
+ text: I18n.get(errorCode),
+ answers: [
+ {label: 'backToMain', action: function() {window.location = '..';}}
+ ]
+ });
+ }
+}
+
+function getSavedState(gameID) {
+ var gameState = Save.get('games.state.Game#' + gameID);
+ return gameState != undefined ? Async.wrap(gameState) : fail('gameNotFound');
+}
+
+function startSession() {
+ var name = Save.get('player.name');
+ if(name != undefined) {
+ Session.start(name);
+ return Async.wrap();
+ } else {
+ return fail('noNameToPlay');
+ }
+}
+
+function initMessageHandlers() {
+ window.addEventListener('focus', catchUp);
Messaging.addEventListener(["Game"], function(o) {
- queue.push(handleGameMessage(o));
- if(document.hasFocus() && queue.length == 1) {
- runQueue();
+ delay(handleGameMessage(o));
+ if(document.hasFocus() && getQueue().length == 1) {
+ catchUp();
} else {
StatusHandler.set("♪");
}
});
- Async.run(setGame({tag: "Game", state: state, logs: []}));
-}
-
-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 {
- 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) {
- var length = queue.length;
- Async.run.apply(null, queue.concat(
- Async.apply(function() {
- queue = queue.slice(length);
- runQueue();
- })
- ));
- }
}
function handleGameMessage(o) {
@@ -88,8 +91,8 @@ function setGame(o) {
setStatus(o.state);
setCaptures(o.state);
[
- [sets.river, o.state.public.river, RiverCard],
- [sets.you.hand, o.state.playerHand, HandCard]
+ [sets.river, o.state.public.river, RiverCard.make],
+ [sets.you.hand, o.state.playerHand, HandCard.make]
].forEach(function(args) {setCardSet.apply(null, args)});
setTheirCards(o.state);
handleStep(o)(f);
@@ -99,13 +102,13 @@ function setGame(o) {
function handleStep(o) {
return function(f) {
handleTurnedCard(o, f);
- if(status.step == "Scored") {
- if(status.playing) {
+ if(state.step == "Scored") {
+ if(state.playing) {
askKoikoi(o, f);
} else {
theyScored(o, f);
}
- } else if (status.step == "Over") {
+ } else if (state.step == "Over") {
gameEnd(o, f);
} else {
f();
@@ -114,10 +117,10 @@ function handleStep(o) {
}
function handleTurnedCard(o, f) {
- if(status.step == "Turned") {
+ if(state.step == "Turned") {
setTurned(o.state.public.step.contents);
} else {
- if(status.step == "ToPlay" && o.state.public.playing == o.state.public.oyake) {
+ if(state.step == "ToPlay" && o.state.public.playing == o.state.public.oyake) {
rest.className = ["card", "turn" + o.state.public.turns].join(' ');
}
if(deck.lastChild.id != "rest") {
@@ -134,7 +137,6 @@ function askKoikoi(o, f) {
{label: 'koikoi', action: function() {play({koiKoi: true}); f();}}
]
});
-
}
function theyScored(o, f) {
@@ -176,114 +178,26 @@ function applyDiff(o) {
);
}
-function animate(movement) {
- return Async.bind(
- 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 || new TurnedCard(card.name);
- turnedCard = null;
- movingCards.push([{card: cardSet, dom: deck}, dest, card]);
- }
- return movingCards;
- }),
- function(movingCards) {
- return Async.parallel.apply(null,
- movingCards.map(function(args) { return moveCard.apply(null, args); })
- );
- }
- );
-}
-
-function moveCard(fromSet, toSet, card) {
- var from, originalCard;
- var slot = 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 Async.sequence(
- Async.wait(10),
- Async.apply(function() {
- card.dom.style.left = 0;
- card.dom.style.top = 0;
- }),
- Async.wait(1000),
- 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) {
- Messaging.send({
- tag: "Play",
- move: move,
- onGame: status.game
- });
-}
-
-function matchingInRiver(card) {
- return Fun.mapFilter(
- Fun.of(sets.river.card),
- Fun.defined
- )(Hanafuda.sameMonth(card).map(Fun.proj('name')));
-}
-
function setStatus(game) {
- Dom.clear(status.dom);
- status.game = game;
- status.step = game.public.step.tag;
- if(game.public.month != status.month) {
- status.month = game.public.month;
+ Dom.clear(dom.status);
+ state.game = game;
+ state.step = game.public.step.tag;
+ if(game.public.month != state.month) {
+ state.month = game.public.month;
}
- status.dom.appendChild(
- Dom.make('li', {textContent: I18n.get('monthFlower')(I18n.get(status.month))})
+ dom.status.appendChild(
+ Dom.make('li', {textContent: I18n.get('monthFlower')(I18n.get(state.month))})
);
var turn = null;
- status.playing = Session.is(game.public.playing);
- if(status.playing) {
- sets.you.hand.dom.classList.toggle("yourTurn", status.step == "ToPlay");
+ state.playing = Session.is(game.public.playing);
+ if(state.playing) {
+ sets.you.hand.dom.classList.toggle("yourTurn", state.step == "ToPlay");
turn = I18n.get("yourTurn");
} else {
sets.you.hand.dom.classList.remove("yourTurn");
turn = I18n.get('playing')(players.get(game.public.playing));
}
- status.dom.appendChild(Dom.make('li', {textContent: turn}));
+ dom.status.appendChild(Dom.make('li', {textContent: turn}));
}
function setCaptures(game) {
@@ -296,7 +210,7 @@ function setCaptures(game) {
Dom.clear(byClass[family.class]);
});
game.public.players[key].meld.forEach(function(cardName) {
- var card = new Card(cardName);
+ var card = Card.make(cardName);
byClass[card.value.family.class].appendChild(card.dom);
});
}
@@ -307,7 +221,7 @@ function setCardSet(set, cardNames, constructor) {
set.card = {};
Dom.clear(set.dom);
cardNames.forEach(function(cardName) {
- var card = new constructor(cardName);
+ var card = constructor(cardName);
set.card[cardName] = card;
set.dom.appendChild(card.dom);
});
@@ -315,7 +229,7 @@ function setCardSet(set, cardNames, constructor) {
function setTheirCards(game) {
var turnsTheyPlayed = Math.floor(
- (game.public.turns + (Session.is(game.public.oyake) ? 0 : 1)) / 2
+ (game.public.gameState.turns + (Session.is(game.public.oyake) ? 0 : 1)) / 2
);
Dom.clear(sets.them.hand.dom);
for(var i = 0; i < 8 - turnsTheyPlayed; i++) {
@@ -324,20 +238,8 @@ function setTheirCards(game) {
}
function setTurned(cardName) {
- turnedCard = new TurnedCard(cardName);
- if(status.playing) {
- selected = turnedCard;
- showCandidates(Hanafuda.Card[cardName], true);
+ state.turnedCard = TurnedCard.make(cardName);
+ if(state.playing) {
+ select(turnedCard);
}
}
-
-function showCandidates(card, yes) {
- matchingInRiver(card).forEach(function(riverCard) {riverCard.setCandidate(yes);});
-}
-
-function setSelected(yes) {
- selected = yes ? this : null;
- this.dom.classList.toggle('selected', yes);
- showCandidates(this.value, yes);
-}
-
diff --git a/js/GUI/Screen/Game/Animation.js b/js/GUI/Screen/Game/Animation.js
new file mode 100644
index 0000000..3611ed3
--- /dev/null
+++ b/js/GUI/Screen/Game/Animation.js
@@ -0,0 +1,106 @@
+import * as Card from GUI.Card;
+import * as State from GUI.Screen.Game.State;
+import * as Async from UnitJS.Async;
+import * as Dom from UnitJS.Dom;
+
+var queue = [];
+
+return {
+ animate: animate,
+ catchUp: catchUp,
+ delay: delay,
+ getQueue: getQueue
+};
+
+function animate(movement) {
+ return Async.bind(
+ Async.apply(function() {
+ var card;
+ var movingCards = [];
+ var side = (State.state.playing) ? 'you' : 'them';
+ var dest = State.sets.river;
+ if(movement.captures != undefined) {
+ card = Card.make(movement.played);
+ dest = State.sets[side];
+ movingCards.push([State.sets.river, dest, Card.make(movement.captures)]);
+ } else {
+ card = RiverCard.make(movement.played);
+ }
+ if(movement.source == 'Hand') {
+ movingCards.push([State.sets[side].hand, dest, card]);
+ } else {
+ var cardSet = {};
+ cardSet[card.name] = State.state.turnedCard || TurnedCard.make(card.name);
+ State.state.turnedCard = null;
+ movingCards.push([{card: cardSet, dom: State.dom.deck}, dest, card]);
+ }
+ return movingCards;
+ }),
+ function(movingCards) {
+ return Async.parallel.apply(null,
+ movingCards.map(function(args) { return moveCard.apply(null, args); })
+ );
+ }
+ );
+}
+
+function catchUp() {
+ if(queue.length > 0) {
+ var length = queue.length;
+ Async.run.apply(null, queue.concat(
+ Async.apply(function() {
+ queue = queue.slice(length);
+ catchUp();
+ })
+ ));
+ }
+}
+
+function delay(animation) {
+ queue.push(animation);
+}
+
+function getQueue() {
+ return queue;
+}
+
+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 moveCard(fromSet, toSet, card) {
+ var from, originalCard;
+ var slot = Dom.make('li', {class: ['card', 'slot']});
+ if (fromSet.card[card.name] != undefined) {
+ originalCard = fromSet.card[card.name].dom;
+ delete fromSet.card[card.name];
+ } else {
+ 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 Async.sequence(
+ Async.wait(10),
+ Async.apply(function() {
+ card.dom.style.left = 0;
+ card.dom.style.top = 0;
+ }),
+ Async.wait(1000),
+ Async.apply(function() {
+ fromSet.dom.removeChild(slot);
+ card.dom.classList.remove('moving');
+ })
+ );
+}
diff --git a/js/GUI/Screen/Game/State.js b/js/GUI/Screen/Game/State.js
new file mode 100644
index 0000000..2b06147
--- /dev/null
+++ b/js/GUI/Screen/Game/State.js
@@ -0,0 +1,84 @@
+import Messaging;
+
+var dom = {
+ deck: document.getElementById('deck'),
+ rest: document.getElementById('rest'),
+ status: document.getElementById('status')
+};
+var selected;
+var sets = {};
+var state = {
+ game: null,
+ playing: false,
+ step: null,
+ month: null,
+ turnedCard: null,
+};
+
+return {
+ dom: dom,
+ getSelected: getSelected,
+ init: init,
+ matchingInRiver: matchingInRiver,
+ play: play,
+ select: select,
+ sets: sets,
+ state: state
+};
+
+function init() {
+ buildSets();
+}
+
+function buildSets() {
+ ['river', 'you', 'them'].forEach(function(id) {
+ var setDom = document.getElementById(id);
+ if(setDom.tagName.toLowerCase() == 'ul') {
+ sets[id] = {card: null, dom: setDom};
+ } else {
+ sets[id] = {};
+ for(var i = 0; i < setDom.children.length; i++) {
+ if(setDom.children[i].tagName.toLowerCase() == 'ul') {
+ var child = setDom.children[i].className;
+ sets[id][child] = {card: {}, dom: setDom.children[i]};
+ }
+ }
+ }
+ });
+}
+
+function getSelected() {
+ return selected;
+}
+
+function matchingInRiver(card) {
+ return Fun.mapFilter(
+ Fun.of(sets.river.card),
+ Fun.defined
+ )(Hanafuda.sameMonth(card).map(Fun.proj('name')));
+}
+
+function play(move) {
+ Messaging.send({
+ tag: "Play",
+ move: move,
+ onGame: state.game
+ });
+}
+
+function showCandidates(card, yes) {
+ matchingInRiver(card).forEach(
+ function(riverCard) {riverCard.setCandidate(yes);}
+ );
+}
+
+function select(cardOrFalse) {
+ if(typeof cardOrFalse == 'object') {
+ selected = cardOrFalse;
+ }
+ selected.dom.classList.toggle('selected', !!cardOrFalse);
+ showCandidates(selected.value, !!cardOrFalse);
+ if(cardOrFalse === false) {
+ selected = null;
+ }
+}
diff --git a/js/Hanafuda.js b/js/Hanafuda.js
index c312afa..f3eca26 100644
--- a/js/Hanafuda.js
+++ b/js/Hanafuda.js
@@ -41,6 +41,7 @@ var Card = Object.freeze(
id: i,
family: findFamily(name, i),
flower: Math.floor(i / 4),
+ monthOffset: i % 4,
name: name
};
return o;
@@ -51,7 +52,6 @@ return {
Flower: Flower,
Family: Family,
Card: Card,
- getValue: getValue,
sameMonth: sameMonth
};
@@ -67,11 +67,6 @@ function findFamily(name, i) {
}
}
-function getValue(card) {
- var first = 4 * card.flower;
- return card.id - first;
-}
-
function sameMonth(card) {
var first = 4 * card.flower;
return [0,1,2,3].map(function(i) {return Card[CardNames[first + i]];});
diff --git a/js/Main.js b/js/Main.js
index 778b6e6..3c97537 100644
--- a/js/Main.js
+++ b/js/Main.js
@@ -1,24 +1,11 @@
-import I18n;
-import * as Screen from GUI.Screen;
import * as Login from GUI.Screen.Login;
import * as Hall from GUI.Screen.Hall;
import * as Game from GUI.Screen.Game;
-import Save;
var gamePath = window.location.pathname.match(/\/game\/([0-9A-Fa-f]+)/);
if(gamePath) {
- var gameState = Save.get('games.state.Game#' + gamePath[1]);
- if(gameState != undefined) {
- Game.init(gameState);
- } else {
- Screen.dialog({
- text: I18n.get('gameNotFound'),
- answers: [
- {label: 'backToMain', action: function() {window.location = '..';}}
- ]
- });
- }
+ Game.init(gamePath[1])
} else {
Login.init();
Hall.init();
diff --git a/js/Messaging.js b/js/Messaging.js
index e61becd..ac7d049 100644
--- a/js/Messaging.js
+++ b/js/Messaging.js
@@ -9,8 +9,9 @@ var s = 1000; /* ms */
var keepAlivePeriod = 20;
var reconnectDelay = 1;
var routes = {callbacks: [], children: {}};
+var messagesQueue = [];
var wsHandlers = {
- open: [function() {on = true; reconnectDelay = 1}, ping],
+ open: [function() {on = true; reconnectDelay = 1}, catchUp, ping],
close: [function() {on = false;}, reconnect]
};
@@ -79,9 +80,19 @@ function messageListener(event) {
};
function send(o) {
- ws.send(JSON.stringify(o));
- o.direction = 'client > server';
- log(o);
+ if(isOn()) {
+ ws.send(JSON.stringify(o));
+ o.direction = 'client > server';
+ log(o);
+ } else {
+ messagesQueue.push(o);
+ }
+}
+
+function catchUp() {
+ var messages = messagesQueue;
+ messagesQueue = [];
+ messagesQueue.forEach(send);
}
function log(message) {
diff --git a/js/Translations.js b/js/Translations.js
index 35e0021..4791679 100644
--- a/js/Translations.js
+++ b/js/Translations.js
@@ -28,6 +28,7 @@ return {
return "This month's flower is the " + flower;
},
noGames: "No games being played",
+ noNameToPlay: "Your name isn't set, how about choosing one first ?",
notFound: "No one goes by that name",
notYet: "Not yet",
ok: "Ok",
@@ -88,6 +89,7 @@ return {
return "C'est le mois des " + flower;
},
noGames: "Aucune partie en cours",
+ noNameToPlay: "Et si on commençait par aller choisir un nom pour jouer",
notFound: "Personne ne s'appelle comme ça",
notYet: "Pas pour l'instant",
ok: "Ok",
diff --git a/skin/game.css b/skin/game.css
index d3fe2ff..96ba7f2 100644
--- a/skin/game.css
+++ b/skin/game.css
@@ -20,7 +20,7 @@
}
#game .card {
- background: url("/cards.jpg") no-repeat;
+ background: url("../skin/cards.jpg") no-repeat;
background-size: 400% 1300%;
display: inline-block;
border-radius: 0.5em;