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

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");
},
});
}
}