X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fcomponents%2FBaseGame.vue;h=3573643d22a694219dfb8d6f6b875017679419cd;hb=1c5bfdf23707e893735f185786e9774b2270ace1;hp=b34eae9f2482c2204ae8af649842affb4ca6c337;hpb=89021f181ac0689bbc785ce0ebd9a910e66352b0;p=vchess.git diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index b34eae9f..3573643d 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -1,46 +1,80 @@ @@ -53,13 +87,13 @@ import { getDate } from "@/utils/datetime"; import { processModalClick } from "@/utils/modalClick"; import { getScoreMessage } from "@/utils/scoring"; export default { - name: 'my-base-game', + name: "my-base-game", components: { Board, - MoveList, + MoveList }, // "vr": VariantRules object, describing the game state + rules - props: ["vr","game"], + props: ["vr", "game"], data: function() { return { st: store.state, @@ -71,7 +105,7 @@ export default { cursor: -1, //index of the move just played lastMove: null, firstMoveNumber: 0, //for printing - incheck: [], //for Board + incheck: [] //for Board }; }, watch: { @@ -79,64 +113,68 @@ export default { "game.fenStart": function() { this.re_setVariables(); }, - // Received a new move to play: - "game.moveToPlay": function(newMove) { - if (!!newMove) //if stop + launch new game, get undefined move - this.play(newMove, "receive"); - }, - // ...Or to undo (corr game, move not validated) - "game.moveToUndo": function(move) { - if (!!move) - this.undo(move); - }, }, computed: { showMoves: function() { - return this.game.vname != "Dark" || this.game.score != "*"; + return this.game.score != "*" + ? "all" + : (this.vr ? this.vr.showMoves : "none"); + }, + showTurn: function() { + return ( + this.game.score == '*' && + this.vr && + (this.vr.showMoves != "all" || !this.vr.canFlip) + ); }, turn: function() { - let color = ""; - const L = this.moves.length; - if (L == 0 || this.moves[L-1].color == "b") - color = "White"; - else //if (this.moves[L-1].color == "w") - color = "Black"; - return this.st.tr[color + " to move"]; + if (!this.vr) + return ""; + if (this.vr.showMoves != "all") + return this.st.tr[(this.vr.turn == 'w' ? "White" : "Black") + " to move"] + // Cannot flip: racing king or circular chess + return this.vr.movesCount == 0 && this.game.mycolor == "w" + ? this.st.tr["It's your turn!"] + : ""; + }, + canAnalyze: function() { + return this.game.mode != "analyze" && this.vr && this.vr.canAnalyze; }, - analyze: function() { - return this.game.mode=="analyze" || - // From Board viewpoint, a finished Dark game == analyze (TODO: unclear) - (this.game.vname == "Dark" && this.game.score != "*"); + canFlip: function() { + return this.vr && this.vr.canFlip; }, + allowDownloadPGN: function() { + return this.game.score != "*" || (this.vr && this.vr.showMoves == "all"); + } }, created: function() { - if (!!this.game.fenStart) - this.re_setVariables(); + if (this.game.fenStart) this.re_setVariables(); }, mounted: function() { - [document.getElementById("eogDiv"),document.getElementById("adjuster")] - .forEach(elt => elt.addEventListener("click", processModalClick)); + [ + document.getElementById("eogDiv"), + document.getElementById("adjuster") + ].forEach(elt => elt.addEventListener("click", processModalClick)); // Take full width on small screens: let boardSize = parseInt(localStorage.getItem("boardSize")); - if (!boardSize) - { - boardSize = (window.innerWidth >= 768 - ? Math.min(600, 0.5*window.innerWidth) //heuristic... - : window.innerWidth); + if (!boardSize) { + boardSize = + window.innerWidth >= 768 + ? 0.75 * Math.min(window.innerWidth, window.innerHeight) + : window.innerWidth; } - const movesWidth = (window.innerWidth >= 768 ? 280 : 0); + const movesWidth = window.innerWidth >= 768 ? 280 : 0; document.getElementById("boardContainer").style.width = boardSize + "px"; let gameContainer = document.getElementById("gameContainer"); - gameContainer.style.width = (boardSize + movesWidth) + "px"; - // TODO: find the right formula here: - //document.getElementById("boardSize").value = Math.floor(boardSize / 10); + gameContainer.style.width = boardSize + movesWidth + "px"; + document.getElementById("boardSize").value = + (boardSize * 100) / (window.innerWidth - movesWidth); // timeout to avoid calling too many time the adjust method let timeoutLaunched = false; - window.addEventListener("resize", (e) => { - if (!timeoutLaunched) - { + window.addEventListener("resize", () => { + if (!timeoutLaunched) { timeoutLaunched = true; - setTimeout( () => { + setTimeout(() => { this.adjustBoard(); timeoutLaunched = false; }, 500); @@ -145,29 +183,26 @@ export default { }, methods: { focusBg: function() { - // NOTE: small blue border appears... document.getElementById("baseGame").focus(); }, adjustBoard: function() { const boardContainer = document.getElementById("boardContainer"); - if (!boardContainer) - return; //no board on page + if (!boardContainer) return; //no board on page const k = document.getElementById("boardSize").value; - const movesWidth = (window.innerWidth >= 768 ? 280 : 0); + const movesWidth = window.innerWidth >= 768 ? 280 : 0; const minBoardWidth = 240; //TODO: these 240 and 280 are arbitrary... // Value of 0 is board min size; 100 is window.width [- movesWidth] - const boardSize = minBoardWidth + - k * (window.innerWidth - (movesWidth+minBoardWidth)) / 100; + const boardSize = + minBoardWidth + + (k * (window.innerWidth - (movesWidth + minBoardWidth))) / 100; localStorage.setItem("boardSize", boardSize); boardContainer.style.width = boardSize + "px"; document.getElementById("gameContainer").style.width = - (boardSize + movesWidth) + "px"; + boardSize + movesWidth + "px"; }, handleKeys: function(e) { - if ([32,37,38,39,40].includes(e.keyCode)) - e.preventDefault(); - switch (e.keyCode) - { + if ([32, 37, 38, 39, 40].includes(e.keyCode)) e.preventDefault(); + switch (e.keyCode) { case 37: this.undo(); break; @@ -187,13 +222,10 @@ export default { }, handleScroll: function(e) { // NOTE: since game.mode=="analyze" => no score, next condition is enough - if (this.game.score != "*") - { + if (this.game.score != "*") { e.preventDefault(); - if (e.deltaY < 0) - this.undo(); - else if (e.deltaY > 0) - this.play(); + if (e.deltaY < 0) this.undo(); + else if (e.deltaY > 0) this.play(); } }, showRules: function() { @@ -202,46 +234,50 @@ export default { }, re_setVariables: function() { this.endgameMessage = ""; - this.orientation = this.game.mycolor || "w"; //default orientation for observed games + // "w": default orientation for observed games + this.orientation = this.game.mycolor || "w"; this.moves = JSON.parse(JSON.stringify(this.game.moves || [])); - // Post-processing: decorate each move with color + current FEN: - // (to be able to jump to any position quickly) - let vr_tmp = new V(this.game.fenStart); //vr is already at end of game - this.firstMoveNumber = - Math.floor(V.ParseFen(this.game.fenStart).movesCount / 2); + // Post-processing: decorate each move with color, notation and FEN + let vr_tmp = new V(this.game.fenStart); + const parsedFen = V.ParseFen(this.game.fenStart); + const firstMoveColor = parsedFen.turn; + this.firstMoveNumber = Math.floor(parsedFen.movesCount / 2); this.moves.forEach(move => { - // NOTE: this is doing manually what play() function below achieve, - // but in a lighter "fast-forward" way move.color = vr_tmp.turn; move.notation = vr_tmp.getNotation(move); vr_tmp.play(move); move.fen = vr_tmp.getFen(); }); - if (this.game.fenStart.indexOf(" b ") >= 0 || - (this.moves.length > 0 && this.moves[0].color == "b")) - { + if (firstMoveColor == "b") { // 'end' is required for Board component to check lastMove for e.p. - this.moves.unshift({color: "w", notation: "...", end: {x:-1,y:-1}}); + this.moves.unshift({ + color: "w", + notation: "...", + end: { x: -1, y: -1 } + }); } const L = this.moves.length; - this.cursor = L-1; - this.lastMove = (L > 0 ? this.moves[L-1] : null); - this.incheck = []; + this.cursor = L - 1; + this.lastMove = L > 0 ? this.moves[L - 1] : null; + this.incheck = this.vr.getCheckSquares(this.vr.turn); }, analyzePosition: function() { - const newUrl = "/analyse/" + this.game.vname + - "/?fen=" + this.vr.getFen().replace(/ /g, "_"); - if (this.game.type == "live") - this.$router.push(newUrl); //open in same tab: against cheating... - else - window.open("#" + newUrl); //open in a new tab: more comfortable + const newUrl = + "/analyse/" + + this.game.vname + + "/?fen=" + + this.vr.getFen().replace(/ /g, "_"); + // Open in same tab in live games (against cheating) + if (this.game.type == "live") this.$router.push(newUrl); + else window.open("#" + newUrl); }, download: function() { const content = this.getPgn(); // Prepare and trigger download link let downloadAnchor = document.getElementById("download"); downloadAnchor.setAttribute("download", "game.pgn"); - downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content); + downloadAnchor.href = + "data:text/plain;charset=utf-8," + encodeURIComponent(content); downloadAnchor.click(); }, getPgn: function() { @@ -255,15 +291,13 @@ export default { pgn += '[Result "' + this.game.score + '"]\n\n'; let counter = 1; let i = 0; - while (i < this.moves.length) - { - pgn += (counter++) + "."; - for (let color of ["w","b"]) - { + while (i < this.moves.length) { + pgn += counter++ + "."; + for (let color of ["w", "b"]) { let move = ""; while (i < this.moves.length && this.moves[i].color == color) move += this.moves[i++].notation + ","; - move = move.slice(0,-1); //remove last comma + move = move.slice(0, -1); //remove last comma pgn += move + (i < this.moves.length ? " " : ""); } } @@ -273,37 +307,35 @@ export default { this.endgameMessage = message; let modalBox = document.getElementById("modalEog"); modalBox.checked = true; - setTimeout(() => { modalBox.checked = false; }, 2000); + setTimeout(() => { + modalBox.checked = false; + }, 2000); }, animateMove: function(move, callback) { let startSquare = document.getElementById(getSquareId(move.start)); - // TODO: error "flush nextTick callbacks" when observer reloads page: - // this late check is not a fix! - if (!startSquare) - return; let endSquare = document.getElementById(getSquareId(move.end)); let rectStart = startSquare.getBoundingClientRect(); let rectEnd = endSquare.getBoundingClientRect(); - let translation = {x:rectEnd.x-rectStart.x, y:rectEnd.y-rectStart.y}; - let movingPiece = - document.querySelector("#" + getSquareId(move.start) + " > img.piece"); - if (!movingPiece) //TODO: shouldn't happen - return; + let translation = { + x: rectEnd.x - rectStart.x, + y: rectEnd.y - rectStart.y + }; + let movingPiece = document.querySelector( + "#" + getSquareId(move.start) + " > img.piece" + ); // HACK for animation (with positive translate, image slides "under background") // Possible improvement: just alter squares on the piece's way... const squares = document.getElementsByClassName("board"); - for (let i=0; i { - for (let i=0; i { + for (let i = 0; i < squares.length; i++) squares.item(i).style.zIndex = "auto"; movingPiece.style = {}; //required e.g. for 0-0 with KR swap callback(); @@ -314,22 +346,21 @@ export default { const navigate = !move; // Forbid playing outside analyze mode, except if move is received. // Sufficient condition because Board already knows which turn it is. - if (!navigate && this.game.mode!="analyze" && !receive - && (this.game.score != "*" || this.cursor < this.moves.length-1)) - { + if ( + !navigate && + this.game.mode != "analyze" && + !receive && + (this.game.score != "*" || this.cursor < this.moves.length - 1) + ) { return; } const doPlayMove = () => { - if (!!receive && this.cursor < this.moves.length-1) - this.gotoEnd(); //required to play the move - if (navigate) - { - if (this.cursor == this.moves.length-1) - return; //no more moves - move = this.moves[this.cursor+1]; - } - else - { + // To play a move, cursor must be at the end of the game: + if (!!receive && this.cursor < this.moves.length - 1) this.gotoEnd(); + if (navigate) { + if (this.cursor == this.moves.length - 1) return; //no more moves + move = this.moves[this.cursor + 1]; + } else { move.color = this.vr.turn; move.notation = this.vr.getNotation(move); } @@ -337,87 +368,77 @@ export default { this.cursor++; this.lastMove = move; if (this.st.settings.sound == 2) - new Audio("/sounds/move.mp3").play().catch(err => {}); - if (!navigate) - { + new Audio("/sounds/move.mp3").play().catch(() => {}); + if (!navigate) { move.fen = this.vr.getFen(); // Stack move on movesList at current cursor - if (this.cursor == this.moves.length) - this.moves.push(move); - else - this.moves = this.moves.slice(0,this.cursor).concat([move]); + if (this.cursor == this.moves.length) this.moves.push(move); + else this.moves = this.moves.slice(0, this.cursor).concat([move]); } // Is opponent in check? this.incheck = this.vr.getCheckSquares(this.vr.turn); const score = this.vr.getCurrentScore(); - if (score != "*") - { + if (score != "*") { const message = getScoreMessage(score); if (this.game.mode != "analyze") this.$emit("gameover", score, message); - else //just show score on screen (allow undo) - this.showEndgameMsg(score + " . " + message); + //just show score on screen (allow undo) + else this.showEndgameMsg(score + " . " + message); } - if (!navigate && this.game.mode!="analyze") + if (!navigate && this.game.mode != "analyze") this.$emit("newmove", move); //post-processing (e.g. computer play) }; - if (!!receive && this.game.vname != "Dark") + if (!!receive && V.ShowMoves == "all") this.animateMove(move, doPlayMove); - else - doPlayMove(); + else doPlayMove(); }, undo: function(move) { const navigate = !move; - if (navigate) - { - if (this.cursor < 0) - return; //no more moves + if (navigate) { + if (this.cursor < 0) return; //no more moves move = this.moves[this.cursor]; } this.vr.undo(move); this.cursor--; - this.lastMove = (this.cursor >= 0 ? this.moves[this.cursor] : undefined); + this.lastMove = this.cursor >= 0 ? this.moves[this.cursor] : undefined; if (this.st.settings.sound == 2) - new Audio("/sounds/undo.mp3").play().catch(err => {}); + new Audio("/sounds/undo.mp3").play().catch(() => {}); this.incheck = this.vr.getCheckSquares(this.vr.turn); - if (!navigate) - this.moves.pop(); + if (!navigate) this.moves.pop(); }, gotoMove: function(index) { this.vr.re_init(this.moves[index].fen); this.cursor = index; this.lastMove = this.moves[index]; + this.incheck = this.vr.getCheckSquares(this.vr.turn); }, gotoBegin: function() { - if (this.cursor == -1) - return; + if (this.cursor == -1) return; this.vr.re_init(this.game.fenStart); - if (this.moves.length > 0 && this.moves[0].notation == "...") - { + if (this.moves.length > 0 && this.moves[0].notation == "...") { this.cursor = 0; this.lastMove = this.moves[0]; - } - else - { + } else { this.cursor = -1; this.lastMove = null; } + this.incheck = this.vr.getCheckSquares(this.vr.turn); }, gotoEnd: function() { - if (this.cursor == this.moves.length - 1) - return; - this.gotoMove(this.moves.length-1); + if (this.cursor == this.moves.length - 1) return; + this.gotoMove(this.moves.length - 1); }, flip: function() { this.orientation = V.GetOppCol(this.orientation); - }, - }, + } + } };