+ type: gtype,
+ increment: tc.increment,
+ mycolor: mycolor,
+ // opponent sid not strictly required (or available), but easier
+ // at least oppsid or oppid is available anyway:
+ oppsid: myIdx < 0 ? undefined : game.players[1 - myIdx].sid,
+ oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].id
+ },
+ game
+ );
+ this.$refs["basegame"].re_setVariables(this.game);
+ if (!this.gameIsLoading) {
+ // Initial loading:
+ this.gotMoveIdx = game.moves.length - 1;
+ // If we arrive here after 'nextGame' action, the board might be hidden
+ let boardDiv = document.querySelector(".game");
+ if (!!boardDiv && boardDiv.style.visibility == "hidden")
+ boardDiv.style.visibility = "visible";
+ }
+ this.re_setClocks();
+ this.$nextTick(() => {
+ this.game.rendered = true;
+ // Did lastate arrive before game was rendered?
+ if (this.lastate) this.processLastate();
+ });
+ if (this.lastateAsked) {
+ this.lastateAsked = false;
+ this.sendLastate(game.oppsid);
+ }
+ if (this.gameIsLoading) {
+ this.gameIsLoading = false;
+ if (this.gotMoveIdx >= game.moves.length)
+ // Some moves arrived meanwhile...
+ this.askGameAgain();
+ }
+ if (!!callback) callback();
+ },
+ fetchGame: function(game, callback) {
+ const afterRetrieval = async (game) => {
+ await import("@/variants/" + game.vname + ".js")
+ .then((vModule) => {
+ window.V = vModule[game.vname + "Rules"];
+ this.loadGame(game, callback);
+ });
+ };
+ if (!!game) {
+ afterRetrieval(game);
+ return;
+ }
+ if (this.gameRef.rid)
+ // Remote live game: forgetting about callback func... (TODO: design)
+ this.send("askfullgame", { target: this.gameRef.rid });
+ else {
+ // Local or corr game on server.
+ // NOTE: afterRetrieval() is never called if game not found
+ const gid = this.gameRef.id;
+ if (Number.isInteger(gid) || !isNaN(parseInt(gid))) {
+ // corr games identifiers are integers
+ ajax(
+ "/games",
+ "GET",
+ {
+ data: { gid: gid },
+ success: (res) => {
+ res.game.moves.forEach(m => {
+ m.squares = JSON.parse(m.squares);
+ });
+ afterRetrieval(res.game);
+ }
+ }
+ );
+ }
+ else
+ // Local game
+ GameStorage.get(this.gameRef.id, afterRetrieval);
+ }
+ },
+ re_setClocks: function() {
+ if (this.game.moves.length < 2 || this.game.score != "*") {
+ // 1st move not completed yet, or game over: freeze time
+ this.virtualClocks = this.game.clocks.map(s => ppt(s).split(':'));
+ return;
+ }
+ const currentTurn = this.vr.turn;
+ const currentMovesCount = this.game.moves.length;
+ const colorIdx = ["w", "b"].indexOf(currentTurn);
+ let countdown =
+ this.game.clocks[colorIdx] -
+ (Date.now() - this.game.initime[colorIdx]) / 1000;
+ this.virtualClocks = [0, 1].map(i => {
+ const removeTime =
+ i == colorIdx ? (Date.now() - this.game.initime[colorIdx]) / 1000 : 0;
+ return ppt(this.game.clocks[i] - removeTime).split(':');
+ });
+ this.clockUpdate = setInterval(
+ () => {
+ if (
+ countdown < 0 ||
+ this.game.moves.length > currentMovesCount ||
+ this.game.score != "*"
+ ) {
+ clearInterval(this.clockUpdate);
+ if (countdown < 0)
+ this.gameOver(
+ currentTurn == "w" ? "0-1" : "1-0",
+ "Time"
+ );
+ } else
+ this.$set(
+ this.virtualClocks,
+ colorIdx,
+ ppt(Math.max(0, --countdown)).split(':')
+ );
+ },
+ 1000
+ );
+ },
+ // 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;
+ } else {
+ // In corr games, just reset clock to mainTime:
+ 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",