+ // TODO: send stack receive stack, or allow incremental? (good/bad points)
+ buildMoveStack(move, r) {
+ this.moveStack.push(move);
+ this.computeNextMove(move);
+ const then = () => {
+ const newTurn = this.turn;
+ if (this.moveStack.length == 1 && !this.hideMoves)
+ this.playVisual(move, r);
+ if (move.next) {
+ this.gameState = {
+ fen: this.getFen(),
+ board: JSON.parse(JSON.stringify(this.board)) //easier
+ };
+ this.buildMoveStack(move.next, r);
+ }
+ else {
+ if (this.moveStack.length == 1) {
+ // Usual case (one normal move)
+ this.afterPlay(this.moveStack, newTurn, {send: true, res: true});
+ this.moveStack = [];
+ }
+ else {
+ this.afterPlay(this.moveStack, newTurn, {send: true, res: false});
+ this.re_initFromFen(this.gameState.fen, this.gameState.board);
+ this.playReceivedMove(this.moveStack.slice(1), () => {
+ this.afterPlay(this.moveStack, newTurn, {send: false, res: true});
+ this.moveStack = [];
+ });
+ }
+ }
+ };
+ // If hiding moves, then they are revealed in play() with callback
+ this.play(move, this.hideMoves ? then : null);
+ if (!this.hideMoves)
+ then();
+ }
+
+ // Implemented in variants using (automatic) moveStack
+ computeNextMove(move) {}
+
+ animateMoving(start, end, drag, segments, cb) {
+ let initPiece = this.getDomPiece(start.x, start.y);
+ // NOTE: cloning often not required, but light enough, and simpler
+ let movingPiece = initPiece.cloneNode();
+ initPiece.style.opacity = "0";
+ let container =
+ document.getElementById(this.containerId)
+ const r = container.querySelector(".chessboard").getBoundingClientRect();
+ if (typeof start.x == "string") {
+ // Need to bound width/height (was 100% for reserve pieces)
+ const pieceWidth = this.getPieceWidth(r.width);
+ movingPiece.style.width = pieceWidth + "px";
+ movingPiece.style.height = pieceWidth + "px";
+ }
+ const maxDist = this.getMaxDistance(r);
+ const apparentColor = this.getColor(start.x, start.y);
+ const pieces = this.pieces(apparentColor, start.x, start.y);
+ if (drag) {
+ const startCode = this.getPiece(start.x, start.y);
+ C.RemoveClass_es(movingPiece, pieces[startCode]["class"]);
+ C.AddClass_es(movingPiece, pieces[drag.p]["class"]);
+ if (apparentColor != drag.c) {
+ movingPiece.classList.remove(C.GetColorClass(apparentColor));
+ movingPiece.classList.add(C.GetColorClass(drag.c));
+ }
+ }
+ container.appendChild(movingPiece);
+ const animateSegment = (index, cb) => {
+ // NOTE: move.drag could be generalized per-segment (usage?)
+ const [i1, j1] = segments[index][0];
+ const [i2, j2] = segments[index][1];
+ const dep = this.getPixelPosition(i1, j1, r);
+ const arr = this.getPixelPosition(i2, j2, r);
+ movingPiece.style.transitionDuration = "0s";
+ movingPiece.style.transform = `translate(${dep[0]}px, ${dep[1]}px)`;
+ const distance =
+ Math.sqrt((arr[0] - dep[0]) ** 2 + (arr[1] - dep[1]) ** 2);
+ const duration = 0.2 + (distance / maxDist) * 0.3;
+ // TODO: unclear why we need this new delay below:
+ setTimeout(() => {
+ movingPiece.style.transitionDuration = duration + "s";
+ // movingPiece is child of container: no need to adjust coordinates
+ movingPiece.style.transform = `translate(${arr[0]}px, ${arr[1]}px)`;
+ setTimeout(cb, duration * 1000);
+ }, 50);
+ };
+ let index = 0;
+ const animateSegmentCallback = () => {
+ if (index < segments.length)
+ animateSegment(index++, animateSegmentCallback);
+ else {
+ movingPiece.remove();
+ initPiece.style.opacity = "1";
+ cb();
+ }
+ };
+ animateSegmentCallback();