<template lang="pug">
main
+ input#modalInfo.modal(type="checkbox")
+ div#infoDiv(
+ role="dialog"
+ data-checkbox="modalInfo"
+ )
+ .card.text-center
+ label.modal-close(for="modalInfo")
+ p(v-html="infoMessage")
input#modalChat.modal(
type="checkbox"
@click="resetChatColor()"
img(src="/images/icons/resign.svg")
button.tooltip(
v-else-if="!!game.mycolor"
- @click="rematch()"
+ @click="clickRematch()"
+ :class="{['rematch-' + rematchOffer]: true}"
:aria-label="st.tr['Rematch']"
)
img(src="/images/icons/rematch.svg")
virtualClocks: [],
vr: null, //"variant rules" object initialized from FEN
drawOffer: "",
+ rematchOffer: "",
people: {}, //players + observers
onMygames: [], //opponents (or me) on "MyGames" page
lastate: undefined, //used if opponent send lastate before game is ready
this.virtualClocks = [[0,0], [0,0]];
this.vr = null;
this.drawOffer = "";
+ this.rematchOffer = "";
this.onMygames = [];
this.lastate = undefined;
this.newChat = "";
)
);
},
+ getOppsid: function() {
+ let oppsid = this.game.oppsid;
+ if (!oppsid) {
+ oppsid = Object.keys(this.people).find(
+ sid => this.people[sid].id == this.game.oppid
+ );
+ }
+ // oppsid is useful only if opponent is online:
+ if (!!oppsid && !!this.people[oppsid]) return oppsid;
+ return null;
+ },
resetChatColor: function() {
// TODO: this is called twice, once on opening an once on closing
document.getElementById("chatBtn").classList.remove("somethingnew");
this.$router.push(
"/game/" + nextGid + "/?next=" + JSON.stringify(this.nextIds));
},
- rematch: function() {
- alert("Unimplemented yet (soon :) )");
- // TODO: same logic as for draw, but re-click remove rematch offer (toggle)
- },
askGameAgain: function() {
this.gameIsLoading = true;
const currentUrl = document.location.href;
.filter(k =>
[
"id","fen","players","vid","cadence","fenStart","vname",
- "moves","clocks","initime","score","drawOffer"
+ "moves","clocks","initime","score","drawOffer","rematchOffer"
].includes(k))
.reduce(
(obj, k) => {
if (
(this.game.moves.length > 0 && this.vr.turn != this.game.mycolor) ||
this.game.score != "*" ||
- this.drawOffer == "sent"
+ this.drawOffer == "sent" ||
+ this.rematchOffer == "sent"
) {
// Send our "last state" informations to opponent
const L = this.game.moves.length;
// Since we played a move (or abort or resign),
// only drawOffer=="sent" is possible
drawSent: this.drawOffer == "sent",
+ rematchSent: this.rematchOffer == "sent",
score: this.game.score,
score: this.game.scoreMsg,
movesCount: L,
// NOTE: observers don't know who offered draw
this.drawOffer = "received";
break;
+ case "rematchoffer":
+ // NOTE: observers don't know who offered rematch
+ this.rematchOffer = data.data ? "received" : "";
+ break;
+ case "newgame": {
+ // A game started, redirect if I'm playing in
+ const gameInfo = data.data;
+ if (
+ gameInfo.players.some(p =>
+ p.sid == this.st.user.sid || p.uid == this.st.user.id)
+ ) {
+ this.$router.push("/game/" + gameInfo.id);
+ } else {
+ let urlRid = "";
+ if (gameInfo.cadence.indexOf('d') === -1) {
+ urlRid = "/?rid=";
+ // Select sid of any of the online players:
+ let onlineSid = [];
+ gameInfo.players.forEach(p => {
+ if (!!this.people[p.sid]) onlineSid.push(p.sid);
+ });
+ urlRid += onlineSid[Math.floor(Math.random() * onlineSid.length)];
+ }
+ this.infoMessage =
+ this.st.tr["Rematch in progress:"] +
+ " <a href='#/game/" +
+ gameInfo.id + urlRid +
+ "'>" +
+ "#/game/" +
+ gameInfo.id + urlRid +
+ "</a>";
+ document.getElementById("modalInfo").checked = true;
+ }
+ break;
+ }
case "newchat":
this.newChat = data.data;
if (!document.getElementById("modalChat").checked)
this.processMove(data.lastMove, { clock: data.clock });
}
if (data.drawSent) this.drawOffer = "received";
+ if (data.rematchSent) this.rematchOffer = "received";
if (data.score != "*") {
this.drawOffer = "";
if (this.game.score == "*")
} else this.updateCorrGame({ drawOffer: this.game.mycolor });
}
},
+ clickRematch: function() {
+ if (!this.game.mycolor) return; //I'm just spectator
+ if (this.rematchOffer == "received") {
+ // Start a new game!
+ let gameInfo = {
+ id: getRandString(), //ignored if corr
+ fen: V.GenRandInitFen(this.game.randomness),
+ players: this.game.players.reverse(),
+ vid: this.game.vid,
+ cadence: this.game.cadence
+ };
+ let oppsid = this.getOppsid(); //may be null
+ this.send("rnewgame", { data: gameInfo, oppsid: oppsid });
+ if (this.game.type == "live") {
+ const game = Object.assign(
+ {},
+ gameInfo,
+ {
+ // (other) Game infos: constant
+ fenStart: gameInfo.fen,
+ vname: this.game.vname,
+ created: Date.now(),
+ // Game state (including FEN): will be updated
+ moves: [],
+ clocks: [-1, -1], //-1 = unstarted
+ initime: [0, 0], //initialized later
+ score: "*"
+ }
+ );
+ GameStorage.add(game, (err) => {
+ // No error expected.
+ if (!err) {
+ if (this.st.settings.sound)
+ new Audio("/sounds/newgame.flac").play().catch(() => {});
+ this.$router.push("/game/" + gameInfo.id);
+ }
+ });
+ }
+ else {
+ // corr game
+ ajax(
+ "/games",
+ "POST",
+ {
+ // cid is useful to delete the challenge:
+ data: { gameInfo: gameInfo },
+ success: (response) => {
+ gameInfo.id = response.gameId;
+ this.$router.push("/game/" + response.gameId);
+ }
+ }
+ );
+ }
+ } else if (this.rematchOffer == "") {
+ this.rematchOffer = "sent";
+ this.send("rematchoffer", { data: true });
+ if (this.game.type == "live") {
+ GameStorage.update(
+ this.gameRef.id,
+ { rematchOffer: this.game.mycolor }
+ );
+ } else this.updateCorrGame({ rematchOffer: this.game.mycolor });
+ } else if (this.rematchOffer == "sent") {
+ // Toggle rematch offer (on --> off)
+ this.rematchOffer = "";
+ this.send("rematchoffer", { data: false });
+ if (this.game.type == "live") {
+ GameStorage.update(
+ this.gameRef.id,
+ { rematchOffer: '' }
+ );
+ } else this.updateCorrGame({ rematchOffer: 'n' });
+ }
+ },
abortGame: function() {
if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"])) return;
this.gameOver("?", "Stop");
}
}
}
+ // TODO: merge next 2 "if" conditions
if (!!game.drawOffer) {
if (game.drawOffer == "t")
// Three repetitions
}
}
}
+ if (!!game.rematchOffer) {
+ if (myIdx < 0) this.rematchOffer = "received";
+ else {
+ // I play in this game:
+ if (
+ (game.rematchOffer == "w" && myIdx == 0) ||
+ (game.rematchOffer == "b" && myIdx == 1)
+ )
+ this.rematchOffer = "sent";
+ else this.rematchOffer = "received";
+ }
+ }
this.repeat = {}; //reset: scan past moves' FEN:
let repIdx = 0;
let vr_tmp = new V(game.fenStart);
clearInterval(this.retrySendmove);
return;
}
- let oppsid = this.game.players[nextIdx].sid;
- if (!oppsid) {
- oppsid = Object.keys(this.people).find(
- sid => this.people[sid].id == this.game.players[nextIdx].uid
- );
- }
- if (!oppsid || !this.people[oppsid])
+ const oppsid = this.getOppsid();
+ if (!oppsid)
// Opponent is disconnected: he'll ask last state
clearInterval(this.retrySendmove);
else {
</script>
<style lang="sass" scoped>
+#infoDiv > .card
+ padding: 15px 0
+ max-width: 430px
+
.connected
background-color: lightgreen
.draw-threerep, .draw-threerep:hover
background-color: #e4d1fc
+.rematch-sent, .rematch-sent:hover
+ background-color: lightyellow
+
+.rematch-received, .rematch-received:hover
+ background-color: lightgreen
+
.somethingnew
background-color: #c5fefe
}
case "game": //individual request
case "newgame": {
- // NOTE: it may be live or correspondance
const game = data.data;
- // Ignore games where I play (corr games)
+ // Ignore games where I play (will go in MyGames page)
if (game.players.every(p =>
p.sid != this.st.user.sid || p.id != this.st.user.id))
{
break;
}
case "startgame": {
- // New game just started: data contain all information
+ // New game just started, I'm involved
const gameInfo = data.data;
if (this.classifyObject(gameInfo) == "live")
this.startNewGame(gameInfo);
"#/game/" +
gameInfo.id +
"</a>";
- let modalBox = document.getElementById("modalInfo");
- modalBox.checked = true;
+ document.getElementById("modalInfo").checked = true;
}
break;
}
const notifyNewgame = () => {
const oppsid = this.getOppsid(c);
if (!!oppsid)
- //opponent is online
+ // Opponent is online
this.send("startgame", { data: gameInfo, target: oppsid });
- // Send game info (only if live) to everyone except me in this tab
- this.send("newgame", { data: gameInfo });
+ // Send game info (only if live) to everyone except me and opponent
+ // TODO: this double message send could be avoided.
+ this.send("newgame", { data: gameInfo, oppsid: oppsid });
};
if (c.type == "live") {
notifyNewgame();
this.startNewGame(gameInfo);
- } //corr: game only on server
- else {
+ } else {
+ // corr: game only on server
ajax(
"/games",
"POST",
},
// NOTE: for live games only (corr games start on the server)
startNewGame: function(gameInfo) {
- const game = Object.assign({}, gameInfo, {
- // (other) Game infos: constant
- fenStart: gameInfo.fen,
- vname: this.getVname(gameInfo.vid),
- created: Date.now(),
- // Game state (including FEN): will be updated
- moves: [],
- clocks: [-1, -1], //-1 = unstarted
- initime: [0, 0], //initialized later
- score: "*"
- });
- GameStorage.add(game, (err) => {
- // If an error occurred, game is not added: abort
- if (!err) {
- if (this.st.settings.sound)
- new Audio("/sounds/newgame.flac").play().catch(() => {});
- this.$router.push("/game/" + gameInfo.id);
+ const game = Object.assign(
+ {},
+ gameInfo,
+ {
+ // (other) Game infos: constant
+ fenStart: gameInfo.fen,
+ vname: this.getVname(gameInfo.vid),
+ created: Date.now(),
+ // Game state (including FEN): will be updated
+ moves: [],
+ clocks: [-1, -1], //-1 = unstarted
+ initime: [0, 0], //initialized later
+ score: "*"
}
- });
+ );
+ setTimeout(
+ () => {
+ GameStorage.add(game, (err) => {
+ // If an error occurred, game is not added: a tab already
+ // added the game and (if focused) is redirected toward it.
+ // If no error and the tab is hidden: do not show anything.
+ if (!err && !document.hidden) {
+ if (this.st.settings.sound)
+ new Audio("/sounds/newgame.flac").play().catch(() => {});
+ this.$router.push("/game/" + gameInfo.id);
+ }
+ });
+ },
+ document.hidden ? 500 + 1000 * Math.random() : 0
+ );
}
}
};