xxxxxxxxxx
305
let myFont;
let clearRowSound;
let pause = false;
let message = "PAUSE";
let fallInterval = 60;
function preload() {
myFont = loadFont("assets/PublicPixel-z84yD.ttf");
clearRowSound = loadSound("assets/tetrisclear.mp3");
}
const boxSize = 30;
const numDivisionsX = 5;
const numDivisionsY = 10;
const numDivisionsZ = 5;
function getRandomColor() {
return [random(256), random(256), random(256)];
}
class Block {
constructor(x, y, z, fill) {
this.x = x;
this.y = y;
this.z = z;
this.fill = fill;
}
draw() {
push();
translate(this.x, this.y, this.z);
if (this.fill) {
fill(this.fill);
stroke(255);
} else {
noFill();
noStroke();
}
box(boxSize);
pop();
}
}
class Piece {
constructor(center, fill) {
this.fill = fill;
this.center = center;
this.coordinates = [[0, 0, 0]];
this.shape = random(["line", "mini-line", "cross", "l", "thunder", "square", "pyramid", "z", "block"]);
if (this.shape === "line") {
this.coordinates = [this.coordinates, [0, -1, 0], [0, 1, 0]];
} else if (this.shape === "mini-line") {
this.coordinates = [this.coordinates, [0, 1, 0]];
} else if (this.shape === "cross") {
this.coordinates = [this.coordinates, [0, 0, -1], [0, 0, 1], [0, -1, 0], [0, 1, 0]];
} else if (this.shape === "l") {
this.coordinates = [this.coordinates, [0, 0, -1], [0, 0, 1], [0, -1, 1]];
} else if (this.shape === "thunder") {
this.coordinates = [this.coordinates, [0, 0, 1], [0, 1, 1], [0, -1, 0]];
} else if (this.shape === "square") {
this.coordinates = [this.coordinates, [0, 0, 1], [0, 1, 1], [0, 1, 0]];
} else if (this.shape === "pyramid") {
this.coordinates = [this.coordinates, [0, 0, -1], [0, 0, 1], [0, -1, 0]];
} else if (this.shape === "z") {
this.coordinates = [this.coordinates, [0, 1, 1], [0, -1, -1], [0, 1, 0], [0, -1, 0]];
}
// console.log(this.shape);
}
move(direction, blocks) {
const [y, z, x] = this.center;
const [yDir, zDir, xDir] = direction;
const center = [y + yDir, z + zDir, x + xDir];
const [yNew, zNew, xNew] = center;
let newBlocks = blocks.map(z => z.map(x => x.map(block => new Block(block.x, block.y, block.z, block.fill))));
for (let i = 0; i < this.coordinates.length; i++) {
const [yCoo, zCoo, xCoo] = this.coordinates[i];
newBlocks[y + yCoo][z + zCoo][x + xCoo].fill = null;
}
for (let i = 0; i < this.coordinates.length; i++) {
const [yCoo, zCoo, xCoo] = this.coordinates[i];
if (yNew + yCoo < 0 || yNew + yCoo >= numDivisionsY ||
zNew + zCoo < 0 || zNew + zCoo >= numDivisionsZ ||
xNew + xCoo < 0 || xNew + xCoo >= numDivisionsX ||
newBlocks[yNew + yCoo][zNew + zCoo][xNew + xCoo].fill) {
for (let j = 0; j < this.coordinates.length; j++) {
const [yCoo, zCoo, xCoo] = this.coordinates[j];
newBlocks[y + yCoo][z + zCoo][x + xCoo].fill = this.fill;
}
return false;
}
}
this.center = center;
return this.update(newBlocks);
}
update(blocks) {
const [y, z, x] = this.center;
for (let i = 0; i < this.coordinates.length; i++) {
const [yCoo, zCoo, xCoo] = this.coordinates[i];
blocks[y+yCoo][z+zCoo][x+xCoo].fill = this.fill;
}
return blocks;
}
rotateX() {
this.coordinates = this.coordinates.map(([y, z, x]) => [y, -x, z]);
}
rotateY() {
this.coordinates = this.coordinates.map(([y, z, x]) => [z, y, -x]);
}
rotateZ() {
this.coordinates = this.coordinates.map(([y, z, x]) => [-z, y, x]);
}
}
class Grid {
constructor() {
this.score = 0;
this.pieces = [];
this.blocks = [];
this.fallSpeed = 0.1;
this.overflow = false;
this.fallingBlock = false;
for (let divisionY = 0; divisionY < numDivisionsY; divisionY++) {
this.blocks[divisionY] = [];
let y = (-((boxSize * (numDivisionsY - 1)) / 2)) + (divisionY * boxSize);
for (let divisionZ = 0; divisionZ < numDivisionsZ; divisionZ++) {
this.blocks[divisionY][divisionZ] = [];
let z = (-((boxSize * (numDivisionsZ - 1)) / 2)) + (divisionZ * boxSize);
for (let divisionX = 0; divisionX < numDivisionsX; divisionX++) {
let x = (-((boxSize * (numDivisionsX - 1)) / 2)) + (divisionX * boxSize);
if ((divisionY === 0) || (divisionY === numDivisionsY-1)) {
this.blocks[divisionY][divisionZ][divisionX] = new Block(x, y, z, [0, 0, 0, 0]);
} else {
this.blocks[divisionY][divisionZ][divisionX] = new Block(x, y, z, null);
}
this.blocks[divisionY][divisionZ][divisionX].draw();
}
}
}
}
draw() {
for (let divisionY = 0; divisionY < numDivisionsY; divisionY++) {
for (let divisionZ = 0; divisionZ < numDivisionsZ; divisionZ++) {
for (let divisionX = 0; divisionX < numDivisionsX; divisionX++) {
this.blocks[divisionY][divisionZ][divisionX].draw();
}
}
}
}
generateRandomPiece() {
if (!this.overflow) {
this.pieces.push(new Piece([1, (numDivisionsZ-1)/2, (numDivisionsX-1)/2], getRandomColor()));
this.blocks = this.pieces.at(-1).update(this.blocks);
}
}
movePiece(i, dir) {
const moveResult = this.pieces.at(i).move(dir, this.blocks);
if (moveResult) {
this.blocks = moveResult;
} else if ((this.pieces.at(i).center[0] === 0) && (dir[0] === 1)) {
this.overflow = true;
} else if (i === this.pieces.length-1) {
this.fallingBlock = false;
}
}
update() {
for (let i = 0; i < this.pieces.length; i++) {
this.movePiece(i, [1, 0, 0]);
}
if (!this.fallingBlock) {
this.generateRandomPiece();
this.fallingBlock = true;
}
for (let divisionY = 1; divisionY < numDivisionsY-1; divisionY++) {
if (this.isRowFull(divisionY)) {
this.clearRow(divisionY);
}
}
}
clearRow(divisionY) {
for (let divisionZ = 0; divisionZ < numDivisionsZ; divisionZ++) {
for (let divisionX = 0; divisionX < numDivisionsX; divisionX++) {
this.blocks[divisionY][divisionZ][divisionX].fill = null;
}
}
clearRowSound.play();
this.score++;
}
isRowFull(divisionY) {
for (let divisionZ = 0; divisionZ < numDivisionsZ; divisionZ++) {
for (let divisionX = 0; divisionX < numDivisionsX; divisionX++) {
if (!this.blocks[divisionY][divisionZ][divisionX].fill) {
return false;
}
}
}
return true;
}
rotatePiece(i, axis) {
const currentCenter = [this.pieces[i].center];
const currentCoordinates = JSON.parse(JSON.stringify(this.pieces[i].coordinates));
if (axis === 'x') {
this.pieces[i].rotateX();
} else if (axis === 'y') {
this.pieces[i].rotateY();
} else if (axis === 'z') {
this.pieces[i].rotateZ();
}
const newBlocks = this.pieces[i].update(this.blocks);
for (let j = 0; j < this.pieces[i].coordinates.length; j++) {
const [yCoo, zCoo, xCoo] = this.pieces[i].coordinates[j];
const [y, z, x] = [currentCenter[0] + yCoo, currentCenter[1] + zCoo, currentCenter[2] + xCoo];
if (y < 0 || y >= numDivisionsY || z < 0 || z >= numDivisionsZ || x < 0 || x >= numDivisionsX || newBlocks[y][z][x].fill) {
this.pieces[i].center = currentCenter;
this.pieces[i].coordinates = currentCoordinates;
return;
}
}
this.blocks = newBlocks;
}
}
let grid;
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
grid = new Grid();
}
function draw() {
if (!pause) {
background(50);
orbitControl();
grid.draw();
if (frameCount % fallInterval === 0) {
grid.update();
if (grid.overflow) {
message = "GAME OVER";
pause = true;
}
}
displayScore();
} else {
displayMessage();
}
}
function keyPressed() {
if (keyCode === ENTER) {
message = "PAUSE";
if (grid.overflow) {
grid = new Grid();
}
pause = !pause;
}
if (!pause) {
if (grid.fallingBlock) {
if (keyCode === LEFT_ARROW) {
grid.movePiece(-1, [0, 0, -1]);
} else if (keyCode === RIGHT_ARROW) {
grid.movePiece(-1, [0, 0, 1]);
} else if (keyCode === UP_ARROW) {
grid.movePiece(-1, [0, -1, 0]);
} else if (keyCode === DOWN_ARROW) {
grid.movePiece(-1, [0, 1, 0]);
} else if (keyCode === 32) {
while (grid.fallingBlock) {
grid.movePiece(-1, [1, 0, 0]);
}
} else if (key === 'x') {
grid.rotatePiece(-1, 'x');
} else if (key === 'y') {
grid.rotatePiece(-1, 'y');
} else if (key === 'z') {
grid.rotatePiece(-1, 'z');
}
}
}
}
function displayMessage() {
fill(255);
textSize(50);
textAlign(CENTER, CENTER);
text(message, 0, 0);
}
function displayScore() {
fill(255);
textSize(20);
textAlign(CENTER, CENTER);
textFont(myFont);
text("SCORE: " + grid.score, 0, ((numDivisionsY/2)+2) * -boxSize);
}