+ // Update variables and storage after a move:
+ processMove: function(move, data) {
+ if (!data) data = {};
+ const moveCol = this.vr.turn;
+ const doProcessMove = () => {
+ const colorIdx = ["w", "b"].indexOf(moveCol);
+ const nextIdx = 1 - colorIdx;
+ const origMovescount = this.game.moves.length;
+ let addTime = 0; //for live games
+ if (moveCol == this.game.mycolor && !data.receiveMyMove) {
+ if (this.drawOffer == "received")
+ // I refuse draw
+ this.drawOffer = "";
+ if (this.game.type == "live" && origMovescount >= 2) {
+ const elapsed = Date.now() - this.game.initime[colorIdx];
+ // elapsed time is measured in milliseconds
+ addTime = this.game.increment - elapsed / 1000;
+ }
+ }
+ // Update current game object:
+ playMove(move, this.vr);
+ // The move is played: stop clock
+ clearInterval(this.clockUpdate);
+ if (!data.score) {
+ // Received move, score has not been computed in BaseGame (!!noemit)
+ const score = this.vr.getCurrentScore();
+ if (score != "*") this.gameOver(score);
+ }
+ this.game.moves.push(move);
+ this.game.fen = this.vr.getFen();
+ if (this.game.type == "live") {
+ if (!!data.clock) this.game.clocks[colorIdx] = data.clock;
+ else this.game.clocks[colorIdx] += addTime;
+ }
+ // In corr games, just reset clock to mainTime:
+ else {
+ this.game.clocks[colorIdx] = extractTime(this.game.cadence).mainTime;
+ }
+ // NOTE: opponent's initime is reset after "gotmove" is received
+ if (
+ !this.game.mycolor ||
+ moveCol != this.game.mycolor ||
+ !!data.receiveMyMove
+ ) {
+ this.game.initime[nextIdx] = Date.now();
+ }
+ // If repetition detected, consider that a draw offer was received:
+ const fenObj = this.vr.getFenForRepeat();
+ this.repeat[fenObj] =
+ !!this.repeat[fenObj]
+ ? this.repeat[fenObj] + 1
+ : 1;
+ if (this.repeat[fenObj] >= 3) this.drawOffer = "threerep";
+ else if (this.drawOffer == "threerep") this.drawOffer = "";
+ if (!!this.game.mycolor && !data.receiveMyMove) {
+ // NOTE: 'var' to see that variable outside this block
+ var filtered_move = getFilteredMove(move);
+ }
+ if (moveCol == this.game.mycolor && !data.receiveMyMove) {
+ // Notify turn on MyGames page:
+ this.notifyMyGames(
+ "turn",
+ {
+ gid: this.gameRef.id,
+ turn: this.vr.turn
+ }
+ );
+ }
+ // Since corr games are stored at only one location, update should be
+ // done only by one player for each move:
+ if (
+ !!this.game.mycolor &&
+ !data.receiveMyMove &&
+ (this.game.type == "live" || moveCol == this.game.mycolor)
+ ) {
+ let drawCode = "";
+ switch (this.drawOffer) {
+ case "threerep":
+ drawCode = "t";
+ break;
+ case "sent":
+ drawCode = this.game.mycolor;
+ break;
+ case "received":
+ drawCode = V.GetOppCol(this.game.mycolor);
+ break;
+ }
+ if (this.game.type == "corr") {
+ // corr: only move, fen and score
+ this.updateCorrGame({
+ fen: this.game.fen,
+ move: {
+ squares: filtered_move,
+ played: Date.now(),
+ idx: origMovescount
+ },
+ // Code "n" for "None" to force reset (otherwise it's ignored)
+ drawOffer: drawCode || "n"
+ });
+ }
+ else {
+ const updateStorage = () => {
+ GameStorage.update(this.gameRef.id, {
+ fen: this.game.fen,
+ move: filtered_move,
+ moveIdx: origMovescount,
+ clocks: this.game.clocks,
+ initime: this.game.initime,
+ drawOffer: drawCode
+ });
+ };
+ // The active tab can update storage immediately
+ if (!document.hidden) updateStorage();
+ // Small random delay otherwise
+ else setTimeout(updateStorage, 500 + 1000 * Math.random());
+ }
+ }
+ // Send move ("newmove" event) to people in the room (if our turn)
+ if (moveCol == this.game.mycolor && !data.receiveMyMove) {
+ let sendMove = {
+ move: filtered_move,
+ index: origMovescount,
+ // color is required to check if this is my move (if several tabs opened)
+ color: moveCol,
+ cancelDrawOffer: this.drawOffer == ""
+ };
+ if (this.game.type == "live")
+ sendMove["clock"] = this.game.clocks[colorIdx];
+ this.opponentGotMove = false;
+ this.send("newmove", {data: sendMove});
+ // If the opponent doesn't reply gotmove soon enough, re-send move:
+ // Do this at most 2 times, because mpore would mean network issues,
+ // opponent would then be expected to disconnect/reconnect.
+ let counter = 1;
+ const currentUrl = document.location.href;
+ this.retrySendmove = setInterval(
+ () => {
+ if (
+ counter >= 3 ||
+ this.opponentGotMove ||
+ document.location.href != currentUrl //page change
+ ) {
+ clearInterval(this.retrySendmove);
+ return;
+ }
+ const oppsid = this.getOppsid();
+ if (!oppsid)
+ // Opponent is disconnected: he'll ask last state
+ clearInterval(this.retrySendmove);
+ else {
+ this.send("newmove", { data: sendMove, target: oppsid });
+ counter++;
+ }
+ },
+ 1500
+ );
+ }
+ else
+ // Not my move or I'm an observer: just start other player's clock
+ this.re_setClocks();
+ };
+ if (
+ this.game.type == "corr" &&
+ moveCol == this.game.mycolor &&
+ !data.receiveMyMove
+ ) {
+ let boardDiv = document.querySelector(".game");
+ const afterSetScore = () => {
+ doProcessMove();
+ if (this.st.settings.gotonext && this.nextIds.length > 0)
+ this.showNextGame();
+ else {
+ // The board might have been hidden:
+ if (boardDiv.style.visibility == "hidden")
+ boardDiv.style.visibility = "visible";
+ }
+ };
+ let el = document.querySelector("#buttonsConfirm > .acceptBtn");
+ // We may play several moves in a row: in case of, remove listener:
+ let elClone = el.cloneNode(true);
+ el.parentNode.replaceChild(elClone, el);
+ elClone.addEventListener(
+ "click",
+ () => {
+ document.getElementById("modalConfirm").checked = false;
+ if (!!data.score && data.score != "*")
+ // Set score first
+ this.gameOver(data.score, null, afterSetScore);
+ else afterSetScore();
+ }
+ );
+ // PlayOnBoard is enough, and more appropriate for Synchrone Chess
+ V.PlayOnBoard(this.vr.board, move);
+ const position = this.vr.getBaseFen();
+ V.UndoOnBoard(this.vr.board, move);
+ if (["all","byrow"].includes(V.ShowMoves)) {
+ this.curDiag = getDiagram({
+ position: position,
+ orientation: V.CanFlip ? this.game.mycolor : "w"
+ });
+ document.querySelector("#confirmDiv > .card").style.width =
+ boardDiv.offsetWidth + "px";
+ } else {
+ // Incomplete information: just ask confirmation
+ // Hide the board, because otherwise it could reveal infos
+ boardDiv.style.visibility = "hidden";
+ this.moveNotation = getFullNotation(move);
+ }
+ document.getElementById("modalConfirm").checked = true;
+ }
+ else {
+ // Normal situation
+ if (!!data.score && data.score != "*")
+ this.gameOver(data.score, null, doProcessMove);
+ else doProcessMove();
+ }
+ },
+ cancelMove: function() {
+ let boardDiv = document.querySelector(".game");
+ if (boardDiv.style.visibility == "hidden")
+ boardDiv.style.visibility = "visible";
+ document.getElementById("modalConfirm").checked = false;
+ this.$refs["basegame"].cancelLastMove();
+ },
+ // In corr games, callback to change page only after score is set:
+ gameOver: function(score, scoreMsg, callback) {
+ this.game.score = score;
+ if (!scoreMsg) scoreMsg = getScoreMessage(score);
+ this.game.scoreMsg = scoreMsg;
+ this.$set(this.game, "scoreMsg", scoreMsg);
+ const myIdx = this.game.players.findIndex(p => {
+ return p.sid == this.st.user.sid || p.uid == this.st.user.id;
+ });
+ if (myIdx >= 0) {
+ // OK, I play in this game
+ const scoreObj = {
+ score: score,
+ scoreMsg: scoreMsg
+ };
+ if (this.game.type == "live") {
+ GameStorage.update(this.gameRef.id, scoreObj);
+ if (!!callback) callback();
+ }
+ else this.updateCorrGame(scoreObj, callback);
+ // Notify the score to main Hall. TODO: only one player (currently double send)
+ this.send("result", { gid: this.game.id, score: score });
+ // Also to MyGames page (TODO: doubled as well...)
+ this.notifyMyGames(
+ "score",
+ {
+ gid: this.gameRef.id,
+ score: score
+ }
+ );
+ }
+ else if (!!callback) callback();
+ }
+ }