'update'
[vchess.git] / client / src / components / BaseGame.vue
index 5c82b0b..635ca64 100644 (file)
@@ -6,24 +6,26 @@ div#baseGame(tabindex=-1 @click="() => focusBg()" @keydown="handleKeys")
       label.modal-close(for="modalEog")
       h3#eogMessage.section {{ endgameMessage }}
   .row
-    #boardContainer.col-sm-12.col-md-9
-      Board(:vr="vr" :last-move="lastMove" :analyze="analyze"
-        :user-color="game.mycolor" :orientation="orientation"
-        :vname="game.vname" @play-move="play")
-      #controls
-        button(@click="gotoBegin") <<
-        button(@click="() => undo()") <
-        button(@click="flip") &#8645;
-        button(@click="() => play()") >
-        button(@click="gotoEnd") >>
-      #fenDiv(v-if="showFen && !!vr")
-        p(@click="gotoFenContent") {{ vr.getFen() }}
-      #pgnDiv
-        a#download(href="#")
-        button(@click="download") {{ st.tr["Download PGN"] }}
+    .col-sm-12.col-md-9
+      #boardContainer
+        Board(:vr="vr" :last-move="lastMove" :analyze="game.mode=='analyze'"
+          :user-color="game.mycolor" :orientation="orientation"
+          :vname="game.vname" @play-move="play")
+        #controls
+          button(@click="gotoBegin") <<
+          button(@click="() => undo()") <
+          button(@click="flip") &#8645;
+          button(@click="() => play()") >
+          button(@click="gotoEnd") >>
+        #pgnDiv
+          a#download(href="#")
+          button(@click="download") {{ st.tr["Download PGN"] }}
+          button(v-if="game.mode!='analyze'" @click="analyzePosition")
+            | {{ st.tr["Analyze"] }}
     .col-sm-12.col-md-3
-      MoveList(v-if="showMoves"
-        :moves="moves" :cursor="cursor" @goto-move="gotoMove")
+      MoveList(v-if="showMoves" :score="game.score" :message="game.scoreMsg"
+        :firstNum="firstMoveNumber" :moves="moves" :cursor="cursor"
+        @goto-move="gotoMove")
 </template>
 
 <script>
@@ -51,7 +53,7 @@ export default {
       moves: [],
       cursor: -1, //index of the move just played
       lastMove: null,
-      gameHasEnded: false, //to avoid showing end message twice
+      firstMoveNumber: 0, //for printing
     };
   },
   watch: {
@@ -60,27 +62,14 @@ export default {
       this.re_setVariables();
     },
     // Received a new move to play:
-    "game.moveToPlay": function() {
-      this.play(this.game.moveToPlay, "receive", this.game.vname=="Dark");
-    },
-    "game.score": function(score) {
-      if (!this.gameHasEnded && score != "*")
-      {
-        // "false" says "don't bubble up": the parent already knows
-        this.endGame(score, this.game.scoreMsg, false);
-      }
+    "game.moveToPlay": function(newMove) {
+      if (!!newMove) //if stop + launch new game, get undefined move
+        this.play(newMove, "receive");
     },
   },
   computed: {
     showMoves: function() {
-      return true;
-      //return window.innerWidth >= 768;
-    },
-    showFen: function() {
-      return this.game.vname != "Dark" || this.score != "*";
-    },
-    analyze: function() {
-      return this.game.mode == "analyze" || this.score != "*";
+      return this.game.vname != "Dark" || this.game.mode=="analyze";
     },
   },
   created: function() {
@@ -117,12 +106,12 @@ export default {
     re_setVariables: function() {
       this.endgameMessage = "";
       this.orientation = this.game.mycolor || "w"; //default orientation for observed games
-      this.score = this.game.score || "*"; //mutable (if initially "*")
-      this.gameHasEnded = (this.score != "*");
       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);
       this.moves.forEach(move => {
         // NOTE: this is doing manually what play() function below achieve,
         // but in a lighter "fast-forward" way
@@ -141,9 +130,11 @@ export default {
       this.cursor = L-1;
       this.lastMove = (L > 0 ? this.moves[L-1]  : null);
     },
-    gotoFenContent: function(event) {
-      this.$router.push("/analyze/" + this.game.vname +
-        "/?fen=" + event.target.innerText.replace(/ /g, "_"));
+    analyzePosition: function() {
+      const newUrl = "/analyze/" + this.game.vname +
+        "/?fen=" + this.vr.getFen().replace(/ /g, "_");
+      //window.open("#" + newUrl); //to open in a new tab
+      this.$router.push(newUrl); //better
     },
     download: function() {
       const content = this.getPgn();
@@ -161,7 +152,7 @@ export default {
       pgn += '[White "' + this.game.players[0].name + '"]\n';
       pgn += '[Black "' + this.game.players[1].name + '"]\n';
       pgn += '[Fen "' + this.game.fenStart + '"]\n';
-      pgn += '[Result "' + this.score + '"]\n\n';
+      pgn += '[Result "' + this.game.score + '"]\n\n';
       let counter = 1;
       let i = 0;
       while (i < this.moves.length)
@@ -203,16 +194,7 @@ export default {
       modalBox.checked = true;
       setTimeout(() => { modalBox.checked = false; }, 2000);
     },
-    endGame: function(score, message, bubbleUp) {
-      this.gameHasEnded = true;
-      this.score = score;
-      if (!message)
-        message = this.getScoreMessage(score);
-      this.showEndgameMsg(score + " . " + message);
-      if (bubbleUp)
-        this.$emit("gameover", score);
-    },
-    animateMove: function(move) {
+    animateMove: function(move, callback) {
       let startSquare = document.getElementById(getSquareId(move.start));
       let endSquare = document.getElementById(getSquareId(move.end));
       let rectStart = startSquare.getBoundingClientRect();
@@ -237,69 +219,65 @@ export default {
         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
-        this.play(move);
+        callback();
       }, 250);
     },
-    play: function(move, receive, noanimate) {
+    play: function(move, receive) {
+      // NOTE: navigate and receive are mutually exclusive
       const navigate = !move;
-      // Forbid playing outside analyze mode when cursor isn't at moves.length-1
-      // (except if we receive opponent's move, human or computer)
-      if (!navigate && !this.analyze && !receive
+      // 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.cursor < this.moves.length-1)
       {
         return;
       }
-      if (navigate)
-      {
-        if (this.cursor == this.moves.length-1)
-          return; //no more moves
-        move = this.moves[this.cursor+1];
-      }
-      if (!!receive && !noanimate) //opponent move, variant != "Dark"
-      {
-        if (this.cursor < this.moves.length-1)
+      const doPlayMove = () => {
+        if (!!receive && this.cursor < this.moves.length-1)
           this.gotoEnd(); //required to play the move
-        return this.animateMove(move);
-      }
-      if (!navigate)
-      {
-        move.color = this.vr.turn;
-        move.notation = this.vr.getNotation(move);
-      }
-      // Not programmatic, or animation is over
-      this.vr.play(move);
-      this.cursor++;
-      this.lastMove = move;
-      if (this.st.settings.sound == 2)
-        new Audio("/sounds/move.mp3").play().catch(err => {});
-      if (!navigate)
-      {
-        move.fen = this.vr.getFen();
-        if (this.score == "*" || this.analyze)
+        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);
+        }
+        this.vr.play(move);
+        this.cursor++;
+        this.lastMove = move;
+        if (this.st.settings.sound == 2)
+          new Audio("/sounds/move.mp3").play().catch(err => {});
+        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.analyze)
-        this.$emit("newmove", move); //post-processing (e.g. computer play)
-      // Is opponent in check?
-      this.incheck = this.vr.getCheckSquares(this.vr.turn);
-      const score = this.vr.getCurrentScore();
-      if (score != "*")
-      {
-        if (!this.analyze)
-          this.endGame(score, undefined, true);
-        else
+        if (!navigate && this.game.mode != "analyze")
+          this.$emit("newmove", move); //post-processing (e.g. computer play)
+        // Is opponent in check?
+        this.incheck = this.vr.getCheckSquares(this.vr.turn);
+        const score = this.vr.getCurrentScore();
+        if (score != "*")
         {
-          // Just show score on screen (allow undo)
           const message = this.getScoreMessage(score);
-          this.showEndgameMsg(score + " . " + message);
+          if (this.game.mode != "analyze")
+            this.$emit("gameover", score, message);
+          else //just show score on screen (allow undo)
+            this.showEndgameMsg(score + " . " + message);
         }
-      }
+      };
+      if (!!receive && this.game.vname != "Dark")
+        this.animateMove(move, doPlayMove);
+      else
+        doPlayMove();
     },
     undo: function(move) {
       const navigate = !move;
@@ -324,6 +302,8 @@ export default {
       this.lastMove = this.moves[index];
     },
     gotoBegin: function() {
+      if (this.cursor == -1)
+        return;
       this.vr.re_init(this.game.fenStart);
       if (this.moves.length > 0 && this.moves[0].notation == "...")
       {
@@ -337,6 +317,8 @@ export default {
       }
     },
     gotoEnd: function() {
+      if (this.cursor == this.moves.length - 1)
+        return;
       this.gotoMove(this.moves.length-1);
     },
     flip: function() {
@@ -349,7 +331,7 @@ export default {
 <style lang="sass">
 #modal-eog+div .card
   overflow: hidden
-#pgnDiv, #fenDiv
+#pgnDiv
   text-align: center
   margin-left: auto
   margin-right: auto
@@ -366,7 +348,7 @@ export default {
     width: 20%
     margin: 0
 #boardContainer
-  margin-top: 5px
+  //margin-top: 5px
   >div
     margin-left: auto
     margin-right: auto