webclient/js/GUI/Screen/Game.js

255 lines
6.5 KiB
JavaScript

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