Improve autoplay, debug move reception while autoplay and/or analyze is on. Add Ambig...
[vchess.git] / client / src / views / Game.vue
index 5145e58..0d9cba2 100644 (file)
@@ -1,15 +1,35 @@
 <template lang="pug">
 main
-  input#modalInfo.modal(type="checkbox")
-  div#infoDiv(
+  input#modalRules.modal(type="checkbox")
+  div#rulesDiv(
     role="dialog"
-    data-checkbox="modalInfo"
+    data-checkbox="modalRules"
+  )
+    .card
+      label.modal-close(for="modalRules")
+      h4#variantNameInGame(@click="gotoRules") {{ game.vname }}
+      div(v-html="rulesContent")
+  input#modalScore.modal(type="checkbox")
+  div#scoreDiv(
+    role="dialog"
+    data-checkbox="modalScore"
+  )
+    .card.text-center
+      label.modal-close(for="modalScore")
+      p.score-section
+        span.score {{ game.score }}
+        | &nbsp;:&nbsp;
+        span.score-msg {{ st.tr[game.scoreMsg] }}
+  input#modalRematch.modal(type="checkbox")
+  div#rematchDiv(
+    role="dialog"
+    data-checkbox="modalRematch"
   )
     .card.text-center
-      label.modal-close(for="modalInfo")
+      label.modal-close(for="modalRematch")
       a(
         :href="'#/game/' + rematchId"
-        onClick="document.getElementById('modalInfo').checked=false"
+        onClick="document.getElementById('modalRematch').checked=false"
       )
         | {{ st.tr["Rematch in progress"] }}
   input#modalChat.modal(
@@ -56,7 +76,7 @@ main
         button.refuseBtn(@click="cancelMove()")
           span {{ st.tr["Cancel"] }}
   .row
-    #aboveBoard.col-sm-12.col-md-9.col-md-offset-3.col-lg-10.col-lg-offset-2
+    #aboveBoard.col-sm-12
       span.variant-cadence(v-if="game.type!='import'") {{ game.cadence }}
       span.variant-name {{ game.vname }}
       span#nextGame(
@@ -133,6 +153,7 @@ main
             span.time-separator(v-if="!!virtualClocks[0][1]") :
             span.time-right(v-if="!!virtualClocks[0][1]")
               | {{ virtualClocks[0][1] }}
+          span.separator
           span.time(
             v-if="game.score=='*'"
             :class="{yourturn: !!vr && vr.turn == 'b'}"
@@ -161,7 +182,7 @@ import { extractTime } from "@/utils/timeControl";
 import { getRandString } from "@/utils/alea";
 import { getScoreMessage } from "@/utils/scoring";
 import { getFullNotation } from "@/utils/notation";
-import { getDiagram } from "@/utils/printDiagram";
+import { getDiagram, replaceByDiag } from "@/utils/printDiagram";
 import { processModalClick } from "@/utils/modalClick";
 import { playMove, getFilteredMove } from "@/utils/playUndo";
 import { ArrayFun } from "@/utils/array";
@@ -183,6 +204,7 @@ export default {
       // virtualClocks will be initialized from true game.clocks
       virtualClocks: [],
       vr: null, //"variant rules" object initialized from FEN
+      rulesContent: "",
       drawOffer: "",
       rematchId: "",
       rematchOffer: "",
@@ -242,8 +264,12 @@ export default {
           this.toggleChat("close")
         });
       });
-    document.getElementById("infoDiv")
-      .addEventListener("click", processModalClick);
+    ["rulesDiv", "rematchDiv", "scoreDiv"].forEach(
+      (eltName) => {
+        document.getElementById(eltName)
+          .addEventListener("click", processModalClick);
+      }
+    );
     if ("ontouchstart" in window) {
       // Disable tooltips on smartphones:
       document.querySelectorAll("#aboveBoard .tooltip").forEach(elt => {
@@ -292,6 +318,9 @@ export default {
     isLargeScreen: function() {
       return window.innerWidth >= 500;
     },
+    gotoRules: function() {
+      this.$router.push("/variants/" + this.game.vname);
+    },
     participateInChat: function(p) {
       return Object.keys(p.tmpIds).some(x => p.tmpIds[x].focus) && !!p.name;
     },
@@ -333,6 +362,7 @@ export default {
       if (!!chatComp) chatComp.chats = [];
       this.virtualClocks = [[0,0], [0,0]];
       this.vr = null;
+      this.rulesContent = "";
       this.drawOffer = "";
       this.lastateAsked = false;
       this.rematchOffer = "";
@@ -490,7 +520,7 @@ export default {
       }
     },
     getGameType: function(game) {
-      if (!!game.id.match(/^i/)) return "import";
+      if (!!game.id.toString().match(/^i/)) return "import";
       return game.cadence.indexOf("d") >= 0 ? "corr" : "live";
     },
     // Notify something after a new move (to opponent and me on MyGames page)
@@ -545,7 +575,8 @@ export default {
             if (sid != this.st.user.sid) {
               this.send("askidentity", { target: sid });
               this.people[sid] = { tmpIds: data.sockIds[sid] };
-            } else {
+            }
+            else {
               // Complete my tmpIds:
               Object.assign(this.people[sid].tmpIds, data.sockIds[sid]);
             }
@@ -611,6 +642,23 @@ export default {
           // player.tmpIds is already set
           player.name = user.name;
           player.id = user.id;
+          if (this.game.type == "live") {
+            const myGidx =
+              this.game.players.findIndex(p => p.sid == this.st.user.sid);
+            // Sometimes a player name isn't stored yet (TODO: why?)
+            if (
+              myGidx >= 0 &&
+              !this.game.players[1 - myGidx].name &&
+              this.game.players[1 - myGidx].sid == user.sid &&
+              !!user.name
+            ) {
+              this.game.players[1-myGidx].name = user.name;
+              GameStorage.update(
+                this.gameRef,
+                { playerName: { idx: 1 - myGidx, name: user.name } }
+              );
+            }
+          }
           this.$forceUpdate(); //TODO: shouldn't be required
           // If I multi-connect, kill current connexion if no mark (I'm older)
           if (this.newConnect[user.sid]) {
@@ -767,8 +815,7 @@ export default {
                   GameStorage.update(this.gameRef, { drawOffer: "" });
                 }
               }
-              this.$refs["basegame"].play(
-                movePlus.move, "received", null, true);
+              this.$refs["basegame"].play(movePlus.move, "received");
               this.game.clocks[moveColIdx] = movePlus.clock;
               this.processMove(
                 movePlus.move,
@@ -835,7 +882,9 @@ export default {
             this.$router.push("/game/" + gameInfo.id);
           } else {
             this.rematchId = gameInfo.id;
-            document.getElementById("modalInfo").checked = true;
+            document.getElementById("modalRules").checked = false;
+            document.getElementById("modalScore").checked = false;
+            document.getElementById("modalRematch").checked = true;
           }
           break;
         }
@@ -897,7 +946,7 @@ export default {
       this.game.clocks[oppIdx] = data.clock;
       if (data.movesCount > L) {
         // Just got last move from him
-        this.$refs["basegame"].play(data.lastMove, "received", null, true);
+        this.$refs["basegame"].play(data.lastMove, "received");
         this.processMove(data.lastMove);
       } else {
         if (!!this.clockUpdate) clearInterval(this.clockUpdate);
@@ -1042,6 +1091,19 @@ export default {
       const myIdx = game.players.findIndex(p => {
         return p.sid == this.st.user.sid || p.id == this.st.user.id;
       });
+      // Sometimes the name isn't stored yet (TODO: why?)
+      if (
+        myIdx >= 0 &&
+        gtype == "live" &&
+        !game.players[myIdx].name &&
+        !!this.st.user.name
+      ) {
+        game.players[myIdx].name = this.st.user.name;
+        GameStorage.update(
+          game.id,
+          { playerName: { idx: myIdx, name: this.st.user.name } }
+        );
+      }
       // "mycolor" is undefined for observers
       const mycolor = [undefined, "w", "b"][myIdx + 1];
       if (gtype == "corr") {
@@ -1067,9 +1129,10 @@ export default {
           game.clocks = [tc.mainTime, tc.mainTime];
           if (myIdx >= 0) {
             // I play in this live game
-            GameStorage.update(game.id, {
-              clocks: game.clocks
-            });
+            GameStorage.update(
+              game.id,
+              { clocks: game.clocks }
+            );
           }
         } else {
           if (!!game.initime)
@@ -1183,17 +1246,25 @@ export default {
         window.V = vModule[game.vname + "Rules"];
         this.loadGame(game, callback);
       });
+      // (AJAX) Request to get rules content (plain text, HTML)
+      this.rulesContent =
+        require(
+          "raw-loader!@/translations/rules/" +
+          game.vname + "/" +
+          this.st.lang + ".pug"
+        )
+        // Next two lines fix a weird issue after last update (2019-11)
+        .replace(/\\n/g, " ")
+        .replace(/\\"/g, '"')
+        .replace('module.exports = "', "")
+        .replace(/"$/, "")
+        .replace(/(fen:)([^:]*):/g, replaceByDiag);
     },
     // 3 cases for loading a game:
     //  - from indexedDB (running or completed live game I play)
     //  - from server (one correspondance game I play[ed] or not)
     //  - from remote peer (one live game I don't play, finished or not)
     fetchGame: function(callback) {
-      
-console.log("fecth");
-      console.log(this.gameRef);
-      console.log(this.gameRef.match(/^i/));
-
       if (Number.isInteger(this.gameRef) || !isNaN(parseInt(this.gameRef))) {
         // corr games identifiers are integers
         ajax(
@@ -1456,9 +1527,12 @@ console.log("fecth");
           }
         );
         // PlayOnBoard is enough, and more appropriate for Synchrone Chess
-        V.PlayOnBoard(this.vr.board, move);
+        const arMove = (Array.isArray(move) ? move : [move]);
+        for (let i = 0; i < arMove.length; i++)
+          V.PlayOnBoard(this.vr.board, arMove[i]);
         const position = this.vr.getBaseFen();
-        V.UndoOnBoard(this.vr.board, move);
+        for (let i = arMove.length - 1; i >= 0; i--)
+          V.UndoOnBoard(this.vr.board, arMove[i]);
         if (["all","byrow"].includes(V.ShowMoves)) {
           this.curDiag = getDiagram({
             position: position,
@@ -1493,6 +1567,9 @@ console.log("fecth");
       this.game.score = score;
       if (!scoreMsg) scoreMsg = getScoreMessage(score);
       this.game.scoreMsg = scoreMsg;
+      document.getElementById("modalRules").checked = false;
+      // Display result in a un-missable way:
+      document.getElementById("modalScore").checked = true;
       this.$set(this.game, "scoreMsg", scoreMsg);
       const myIdx = this.game.players.findIndex(p => {
         return p.sid == this.st.user.sid || p.id == this.st.user.id;
@@ -1534,10 +1611,27 @@ console.log("fecth");
 </script>
 
 <style lang="sass" scoped>
-#infoDiv > .card
-  padding: 15px 0
+#scoreDiv > .card, #rematchDiv > .card
+  padding: 10px 0
   max-width: 430px
 
+#rulesDiv > .card
+  padding: 5px 0
+  max-width: 50%
+  max-height: 100%
+  @media screen and (max-width: 1500px)
+    max-width: 67%
+  @media screen and (max-width: 1024px)
+    max-width: 85%
+  @media screen and (max-width: 767px)
+    max-width: 100%
+
+p.score-section
+  margin: 0
+  font-size: 1.3em
+  span.score
+    font-weight: bold
+
 .connected
   background-color: lightgreen
 
@@ -1551,9 +1645,6 @@ console.log("fecth");
 #playersInfo > p
   margin: 0
 
-@media screen and (min-width: 768px)
-  #actions
-    width: 300px
 @media screen and (max-width: 767px)
   .game
     width: 100%
@@ -1572,12 +1663,8 @@ button
     @media screen and (max-width: 767px)
       height: 18px
 
-@media screen and (max-width: 767px)
-  #aboveBoard
-    text-align: center
-@media screen and (min-width: 768px)
-  #aboveBoard
-    margin-left: 30%
+#aboveBoard
+  text-align: center
 
 .variant-cadence
   padding-right: 10px
@@ -1592,6 +1679,12 @@ span#nextGame
   display: inline-block
   margin-right: 10px
 
+span.separator
+  display: inline-block
+  margin: 0
+  padding: 0
+  width: 10px
+
 span.name
   font-size: 1.5rem
   padding: 0 3px
@@ -1661,4 +1754,15 @@ button.acceptBtn
   background-color: lightgreen
 button.refuseBtn
   background-color: red
+
+h4#variantNameInGame
+  cursor: pointer
+  text-align: center
+  text-decoration: underline
+  font-weight: bold
+</style>
+
+<style lang="sass">
+@import "@/styles/_rules.sass"
+@import "@/styles/_board_squares_img.sass"
 </style>