You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.7 KiB
247 lines
6.7 KiB
import Phaser from "phaser";
|
|
import bg from "./bg.jpg";
|
|
import puzzle from "./puzzle.png";
|
|
import countdown from "./countdown.png";
|
|
import "../../font.css";
|
|
|
|
const PUZZLE_WH = 1640;
|
|
const PIECE_WH = 546;
|
|
const OFFSET_LEFT = 260 + PIECE_WH / 2;
|
|
const OFFSET_TOP = 390 + PIECE_WH / 2;
|
|
|
|
const surroundingPieces = (z, w, h) => {
|
|
const x = z % w;
|
|
const y = Math.floor(z / h);
|
|
|
|
const left = x === 0 ? null : z - 1;
|
|
const up = y === 0 ? null : z - w;
|
|
const right = x === w - 1 ? null : z + 1;
|
|
const down = y === h - 1 ? null : z + w;
|
|
|
|
return [left, up, right, down].filter((i) => i !== null);
|
|
};
|
|
const shuffle = (puzzle, moves, width, height) => {
|
|
let empty = puzzle[0];
|
|
let lastPiece = empty;
|
|
|
|
for (let i = 0; i < moves; i++) {
|
|
let pieces = surroundingPieces(empty, width, height);
|
|
pieces = pieces.filter((i) => i !== lastPiece);
|
|
|
|
let pieceIndex = pieces[Math.floor(Math.random() * pieces.length)];
|
|
|
|
puzzle[empty] = puzzle[pieceIndex];
|
|
lastPiece = empty;
|
|
|
|
puzzle[pieceIndex] = 0;
|
|
empty = pieceIndex;
|
|
}
|
|
|
|
return puzzle;
|
|
};
|
|
|
|
const createShuffledIndexArray = (col) => {
|
|
const indexArray = new Array(col * col).fill(0).map((_, i) => i);
|
|
return shuffle(indexArray, 25, col, col);
|
|
};
|
|
|
|
class Scene extends Phaser.Scene {
|
|
constructor() {
|
|
super("scene");
|
|
}
|
|
preload() {
|
|
this.load.image("bg", bg);
|
|
this.load.image("countdown", countdown);
|
|
this.load.image("totalpuzzle", puzzle);
|
|
this.load.spritesheet("puzzle", puzzle, {
|
|
frameWidth: PIECE_WH,
|
|
frameHeight: PIECE_WH,
|
|
});
|
|
}
|
|
create() {
|
|
this.won = false;
|
|
const bg = this.add.image(
|
|
this.game.config.width / 2,
|
|
this.game.config.height / 2,
|
|
"bg"
|
|
);
|
|
bg.displayWidth = this.game.config.width;
|
|
bg.displayHeight = this.game.config.height;
|
|
const countdown = this.add.image(465, 474, "countdown");
|
|
countdown.setDepth(1);
|
|
this.seconds = 300;
|
|
this.secText = this.add.text(520, 450, this.seconds, {
|
|
fontSize: 45,
|
|
fontFamily: "game",
|
|
color: "#429b47",
|
|
});
|
|
this.secText.setDepth(2);
|
|
|
|
const BOARD_COLS = Math.floor(PUZZLE_WH / PIECE_WH);
|
|
const BOARD_ROWS = Math.floor(PUZZLE_WH / PIECE_WH);
|
|
|
|
let piecesAmount = BOARD_COLS * BOARD_ROWS;
|
|
this.time.addEvent({
|
|
delay: 1000,
|
|
loop: true,
|
|
callback: () => {
|
|
if (this.seconds === 0) this.game.onLose && this.game.onLose();
|
|
if (!this.won && this.seconds > 0) this.seconds--;
|
|
this.secText.setText(this.seconds);
|
|
},
|
|
});
|
|
const shuffledIndexArray = createShuffledIndexArray(BOARD_COLS);
|
|
let piecesIndex = 0;
|
|
let piecesGroup = this.add.group();
|
|
|
|
this.piecesGroup = piecesGroup;
|
|
for (let i = 0; i < BOARD_ROWS; i++) {
|
|
for (let j = 0; j < BOARD_COLS; j++) {
|
|
let piece;
|
|
if (shuffledIndexArray[piecesIndex]) {
|
|
piece = piecesGroup.create(
|
|
OFFSET_LEFT + j * PIECE_WH,
|
|
OFFSET_TOP + i * PIECE_WH,
|
|
"puzzle",
|
|
shuffledIndexArray[piecesIndex]
|
|
);
|
|
} else {
|
|
//initial position of black piece
|
|
piece = piecesGroup.create(
|
|
OFFSET_LEFT + j * PIECE_WH,
|
|
OFFSET_TOP + i * PIECE_WH,
|
|
"puzzle",
|
|
shuffledIndexArray[piecesIndex],
|
|
false
|
|
);
|
|
piece.black = true;
|
|
}
|
|
piece.name = "piece" + i.toString() + "x" + j.toString();
|
|
piece.currentIndex = piecesIndex;
|
|
piece.destIndex = shuffledIndexArray[piecesIndex];
|
|
piece.inputEnabled = true;
|
|
piece.posX = j;
|
|
piece.posY = i;
|
|
piece.setInteractive();
|
|
piecesIndex++;
|
|
}
|
|
}
|
|
this.input.on(
|
|
"gameobjectdown",
|
|
(_, piece) => {
|
|
var blackPiece = this.canMove(piece);
|
|
if (blackPiece) {
|
|
this.movePiece(piece, blackPiece);
|
|
}
|
|
},
|
|
this
|
|
);
|
|
}
|
|
canMove(piece) {
|
|
let foundBlackElem = false;
|
|
|
|
this.piecesGroup.getChildren().forEach(function (element) {
|
|
if (
|
|
(element.posX === piece.posX - 1 &&
|
|
element.posY === piece.posY &&
|
|
element.black) ||
|
|
(element.posX === piece.posX + 1 &&
|
|
element.posY === piece.posY &&
|
|
element.black) ||
|
|
(element.posY === piece.posY - 1 &&
|
|
element.posX === piece.posX &&
|
|
element.black) ||
|
|
(element.posY === piece.posY + 1 &&
|
|
element.posX === piece.posX &&
|
|
element.black)
|
|
) {
|
|
foundBlackElem = element;
|
|
return;
|
|
}
|
|
});
|
|
|
|
return foundBlackElem;
|
|
}
|
|
movePiece(piece, blackPiece) {
|
|
var tmpPiece = {
|
|
posX: piece.posX,
|
|
posY: piece.posY,
|
|
currentIndex: piece.currentIndex,
|
|
};
|
|
this.tweens.add({
|
|
targets: piece,
|
|
x: OFFSET_LEFT + blackPiece.posX * PIECE_WH,
|
|
y: OFFSET_TOP + blackPiece.posY * PIECE_WH,
|
|
ease: "linear",
|
|
duration: 300,
|
|
});
|
|
|
|
piece.posX = blackPiece.posX;
|
|
piece.posY = blackPiece.posY;
|
|
piece.currentIndex = blackPiece.currentIndex;
|
|
piece.name = "piece" + piece.posX.toString() + "x" + piece.posY.toString();
|
|
|
|
blackPiece.posX = tmpPiece.posX;
|
|
blackPiece.posY = tmpPiece.posY;
|
|
blackPiece.currentIndex = tmpPiece.currentIndex;
|
|
blackPiece.name =
|
|
"piece" + blackPiece.posX.toString() + "x" + blackPiece.posY.toString();
|
|
this.checkIfFinished();
|
|
}
|
|
checkIfFinished() {
|
|
let isFinished = true;
|
|
|
|
this.piecesGroup.getChildren().forEach((element) => {
|
|
if (element.currentIndex !== element.destIndex) {
|
|
isFinished = false;
|
|
return;
|
|
}
|
|
});
|
|
|
|
if (isFinished) {
|
|
this.won = true;
|
|
this.piecesGroup.getChildren().forEach((piece) => {
|
|
piece.setInteractive(false);
|
|
piece.visible = false;
|
|
});
|
|
const total = this.add.image(
|
|
OFFSET_LEFT - PIECE_WH / 2,
|
|
OFFSET_TOP - PIECE_WH / 2,
|
|
"totalpuzzle"
|
|
);
|
|
total.displayOriginX = 0;
|
|
total.displayOriginY = 0;
|
|
total.displayWidth = PUZZLE_WH;
|
|
total.displayHeight = PUZZLE_WH;
|
|
this.game.onWon && this.game.onWon();
|
|
}
|
|
}
|
|
}
|
|
export default class Game extends Phaser.Game {
|
|
constructor({ containerId, onLose, onWon }) {
|
|
const container = document.getElementById(containerId);
|
|
const containerWH = container.clientWidth;
|
|
super({
|
|
type: Phaser.CANVAS,
|
|
width: containerWH,
|
|
height: containerWH,
|
|
parent: containerId,
|
|
backgroundColor: 0xbbada0,
|
|
scale: {
|
|
mode: Phaser.Scale.FIT,
|
|
autoCenter: Phaser.Scale.CENTER_BOTH,
|
|
parent: containerId,
|
|
width: 2160,
|
|
height: 2160,
|
|
},
|
|
scene: Scene,
|
|
});
|
|
Object.assign(this, {
|
|
onLose,
|
|
onWon,
|
|
restart() {
|
|
this.scene.scenes[0].scene.start("scene");
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|