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, play, select, sets, globalState } from GUI.Screen.Game.State; import Hanafuda; import I18n; import Messaging; import players from Room; import Save; import Session; import StatusHandler; import * as Async from UnitJS.Async; import * as Dom from UnitJS.Dom; import * as Fun from UnitJS.Fun; return { init: init } function init(gameID) { initState(); initMessageHandlers(); Async.run( Async.bind( getSavedStates(gameID), function(states) { return Async.sequence( startSession(), previously(states) ); } ) ); } function backToMain() { window.location = '..'; } function fail(errorCode) { return function(f) { Screen.dialog({ text: I18n.get(errorCode), answers: [{label: 'backToMain', action: backToMain}] }); } } function getSavedStates(gameID) { var states = Save.get('games.state.Game#' + gameID); return states != undefined ? Async.wrap(states) : fail('gameNotFound'); } function startSession() { var name = Save.get('player.name'); if(name != undefined) { Session.start(name); return Async.wrap(); } else { return fail('noNameToPlay'); } } function previously(states) { if(states.former != undefined && states.latest.logs.length > 0) { return Async.sequence( setGame(states.former), handleGameMessage(states.latest) ); } else { return setGame(states.latest); } } function initMessageHandlers() { window.addEventListener('focus', catchUp); Messaging.addEventListener(["Game"], function(o) { if(o.state.public.coordinates.gameID == globalState.game.public.coordinates.gameID) { delay(handleGameMessage(o.state)); if(document.hasFocus() && getQueue().length == 1) { catchUp(); } else { StatusHandler.set("♪"); } } }); } function handleGameMessage(state) { if(state.public.coordinates.turn == 0) { if(state.logs.length > 0) { // but still some logs, from the previous round return Async.sequence(applyDiff(state), setGame(state)); // so play the diff, then set the new round } else { return setGame(state); // directly set a whole new game } } else { return applyDiff(state); } } function setGame(state) { return function(f) { setStatus(state); setCaptures(state); [ [sets.river, state.public.river, RiverCard.make], [sets.you.hand, state.playerHand, HandCard.make] ].forEach(function(args) {setCardSet.apply(null, args)}); setTheirCards(state); handleStep(state)(f); }; } function handleStep(state) { return function(f) { handleTurnedCard(state, f); if(globalState.step == "Scored") { if(globalState.playing) { askKoikoi(state, f); } else { theyScored(state, f); } } else if (globalState.step == "Over") { gameEnd(state, f); } else { f(); } }; } function handleTurnedCard(state, f) { if(globalState.step == "Turned") { setTurned(state.public.step.contents); } else { if(globalState.step == "ToPlay" && state.public.playing == state.public.oyake) { dom.rest.className = ["card", "turn" + state.public.coordinates.turn].join(' '); } if(deck.lastChild.id != "rest") { deck.removeChild(deck.lastChild); } } } function askKoikoi(state, f) { Screen.dialog({ text: I18n.get('youScored'), answers: [ {label: 'endRound', action: function() {play({koiKoi: false}); f();}}, {label: 'koikoi', action: function() {play({koiKoi: true}); f();}} ] }); } function theyScored(state, f) { Screen.dialog({ text: I18n.get('theyScored')(players.get(state.public.playing)), answers: [ {label: 'ok', action: f} ] }); } function gameEnd(state, f) { var winner, maxScore; for(var key in state.public.scores) { if(maxScore == undefined || state.public.scores[key] > maxScore) { winner = key; maxScore = state.public.scores[key]; } } Screen.dialog({ text: I18n.get(Session.is(winner) ? 'won' : 'lost'), answers: [{label: 'endGame', action: backToMain}] }); } function applyDiff(state) { return Async.sequence.apply(null, state.logs.map(animate).concat( Async.apply(setStatus, state), handleStep(state) ) ); } function setStatus(game) { Dom.clear(dom.status); globalState.game = game; globalState.step = game.public.step.tag; if(game.public.coordinates.month != globalState.month) { globalState.month = game.public.coordinates.month; } dom.status.appendChild( Dom.make('li', {textContent: I18n.get('monthFlower')(I18n.get(globalState.month))}) ); var turn = null; globalState.playing = Session.is(game.public.playing); if(globalState.playing) { sets.you.hand.dom.classList.toggle("yourTurn", globalState.step == "ToPlay"); turn = I18n.get("yourTurn"); } else { sets.you.hand.dom.classList.remove("yourTurn"); turn = I18n.get('playing')(players.get(game.public.playing)); } dom.status.appendChild(Dom.make('li', {textContent: turn})); } function setCaptures(game) { for(var key in game.public.players) { var elem = document.getElementById(Session.is(key) ? "you" : "them"); elem.getElementsByClassName('score')[0].textContent = game.public.scores[key] + " pts"; var byClass = {} Object.values(Hanafuda.Family).forEach(function(family) { byClass[family.class] = elem.getElementsByClassName(family.class)[0]; Dom.clear(byClass[family.class]); }); game.public.players[key].meld.forEach(function(cardName) { var card = Card.make(cardName); byClass[card.value.family.class].appendChild(card.dom); }); } } function setCardSet(set, cardNames, constructor) { constructor = constructor || Card.make; set.card = {}; Dom.clear(set.dom); cardNames.forEach(function(cardName) { var card = constructor(cardName); set.card[cardName] = card; set.dom.appendChild(card.dom); }); } function setTheirCards(game) { var turnsTheyPlayed = Math.floor( (game.public.coordinates.turn + (Session.is(game.public.oyake) ? 0 : 1)) / 2 ); Dom.clear(sets.them.hand.dom); for(var i = 0; i < 8 - turnsTheyPlayed; i++) { sets.them.hand.dom.appendChild(Dom.make('li', {class: "card"})); } } function setTurned(cardName) { globalState.turnedCard = TurnedCard.make(cardName); if(globalState.playing) { select(globalState.turnedCard); } }