1/jekyll_site/js/tetris-model.js
2023-12-17 07:55:25 +03:00

245 lines
7.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// © Головин Г.Г., Логика игрового процесса, 2023
'use strict';
// игровое поле и его размеры
let field, rows = 20, columns = 10;
// ускорение падения фигур
const REDUCTION_STEP = 25;
const REDUCTION_SNAIL = 5;
let reduction = REDUCTION_STEP;
// скорость падения фигур
const START_DELAY = 600;
const MIN_DELAY = 80;
let stepDelay = START_DELAY;
let sleepTimeout, rapidFall = false;
// уровень, следующий уровень, счёт
let level, nextLevel, score;
// массив фигур тетрамино
const figures = FIGURE.set();
// текущая фигура, следующая фигура
let currentFigure, nextFigure;
// статусы игры
const GAME = {RUN:0,LEVEL:1,PAUSE:2,OVER:3};
// текущий статус
let status = GAME.PAUSE;
// подготовить новую игру
function prepareNewGame() {
field = [];
for (let i = 0; i < rows; i++) {
field[i] = [];
for (let j = 0; j < columns; j++)
field[i][j] = 0;
}
status = GAME.PAUSE;
level = 0;
score = 0;
nextLevel = (10 * columns) * (level + 1);
stepDelay = START_DELAY;
startFigureFall();
repaint();
}
// начало падения фигуры
function startFigureFall() {
currentFigure = nextFigure;
const x = Math.floor(columns/2+columns%2-1);
if (isFreeSpace(0, x)) {
doPlaceFigure(0, x);
repaint();
} else {
status=GAME.OVER;
for (let y=-1; y>-4; y--)
if (isFreeSpace(y, x, false)) {
doPlaceFigure(y, x);
break;
}
repaint();
return;
}
const rnd = Math.floor(Math.random() * figures.length);
nextFigure = figures[rnd].clone();
if (status==GAME.LEVEL) return;
setTimeout(figureFall, 60);
}
// падение фигуры
function figureFall() {
if (rapidFall)
stepDown();
else
sleepTimeout = setTimeout(stepDown, stepDelay);
}
// шаг вниз
function stepDown() {
sleepTimeout = undefined;
if (status==GAME.PAUSE) return;
if (isSpaceDown()) {
doStepDown();
setTimeout(figureFall, 60);
} else {
mergeFigure(currentFigure.type);
let fullRows = 0;
for (let i = 0; i < rows; i++)
if (isFullRow(i)) {
for (let c = 3; c >= 0; c--)
setTimeout(blinkFullRow, 400 * fullRows + 100 * c, i, c);
setTimeout(slideDown, 400 * fullRows + 400, i);
fullRows++;
}
score += columns * [0,1,3,5,10][fullRows];
if (score >= nextLevel) {
level++;
status = GAME.LEVEL;
nextLevel = (10 * columns) * (level + 1);
stepDelay = Math.max(MIN_DELAY,stepDelay-reduction);
}
startFigureFall();
}
}
// свободное место ниже текущей фигуры
function isSpaceDown() {
for (let y = rows-2; y >= 0; y--)
for (let x = 0; x < columns; x++)
if (field[y][x]==11 && field[y+1][x]>0 && field[y+1][x]<11
|| field[y+1][x]==11 && y==rows-2)
return false;
return true;
}
// сдвинуть текущую фигуру вниз
function doStepDown() {
for (let y = rows-2; y >= 0; y--)
for (let x = 0; x < columns; x++)
if (field[y][x]==11) {
field[y][x]=0;
field[y+1][x]=11;
}
repaint();
}
// завершение движения текущей фигуры
function mergeFigure(type) {
for (let x = 0; x < columns; x++)
for (let y = 0; y < rows; y++)
if (field[y][x] == 11)
field[y][x] = type;
}
// заполненная строка
function isFullRow(row) {
for (let x = 0; x < columns; x++)
if (field[row][x] == 0)
return false;
return true;
}
// моргание заполненной строки
function blinkFullRow(row, color) {
for (let x = 0; x < columns; x++)
field[row][x] = color;
repaint();
}
// сдвинуть поле вниз
function slideDown(row) {
for (let y = row-1; y >= 0; y--)
for (let x = 0; x < columns; x++)
if (field[y+1][x]!=11 && field[y][x]!=11)
field[y+1][x]=field[y][x];
repaint();
}
// свободное место для текущей фигуры в пределах границ поля
function isFreeSpace(y, x, fullSize=true) {
const height = currentFigure.shape.length;
const wight = currentFigure.shape[0].length;
if (fullSize && (y<0 || y+height>rows || x<0 || x+wight>columns))
return false;
for (let yy=0; yy<height; yy++)
for (let xx=0; xx<wight; xx++)
if (currentFigure.shape[yy][xx]>0)
if (y+yy>=0 && y+yy<rows && x+xx>=0 && x+xx<columns)
if (field[y+yy][x+xx]!=0)
return false;
return true;
}
// разместить текущую фигуру в пределах границ поля
function doPlaceFigure(y, x, type=11) {
const height = currentFigure.shape.length;
const wight = currentFigure.shape[0].length;
for (let yy=0; yy<height; yy++)
for (let xx=0; xx<wight; xx++)
if (currentFigure.shape[yy][xx]>0)
if (y+yy>=0 && y+yy<rows && x+xx>=0 && x+xx<columns)
field[y+yy][x+xx]=type;
}
// для вызова из контроллера
function moveFigureLeft() {
if (isSpaceLeft())
doStepLeft();
repaint();
}
// свободное место слева от текущей фигуры
function isSpaceLeft() {
for (let x = 1; x < columns; x++)
for (let y = 0; y < rows; y++)
if (field[y][x]==11 && field[y][x-1]>0 && field[y][x-1]<11
|| field[y][x-1]==11 && x==1)
return false;
return true;
}
// сдвинуть текущую фигуру влево
function doStepLeft() {
for (let x = 1; x < columns; x++)
for (let y = 0; y < rows; y++)
if (field[y][x]==11) {
field[y][x]=0;
field[y][x-1]=11;
}
}
// для вызова из контроллера
function moveFigureRight() {
if (isSpaceRight())
doStepRight();
repaint();
}
// свободное место справа от текущей фигуры
function isSpaceRight() {
for (let x = columns-2; x >=0; x--)
for (let y = 0; y < rows; y++)
if (field[y][x]==11 && field[y][x+1]>0 && field[y][x+1]<11
|| field[y][x+1]==11 && x==columns-2)
return false;
return true;
}
// сдвинуть текущую фигуру вправо
function doStepRight() {
for (let x = columns-2; x >=0; x--)
for (let y = 0; y < rows; y++)
if (field[y][x]==11) {
field[y][x]=0;
field[y][x+1]=11;
}
}
// для вызова из контроллера, поворот фигуры
function rotateFigure() {
let y = rows, x = columns;
for (let yy = 0; yy < rows; yy++)
for (let xx = 0; xx < columns; xx++)
if (field[yy][xx] == 11) {
if (y > yy) y = yy;
if (x > xx) x = xx;
}
if (y == rows || x == columns) return;
const old = currentFigure.shape;
doPlaceFigure(y, x, 0);
currentFigure.rotate();
if (isFreeSpace(y, x))
doPlaceFigure(y, x);
else if (isFreeSpace(y, x-1))
doPlaceFigure(y, x-1);
else {
currentFigure.shape = old;
doPlaceFigure(y, x);
}
repaint();
}
// после загрузки всех частей страницы, запускаем игру
document.addEventListener('DOMContentLoaded', function() {
addEventListener('keydown', keyPressed);
addEventListener('keyup', keyReleased);
const rnd = Math.floor(Math.random() * figures.length);
nextFigure = figures[rnd].clone();
prepareNewGame();
});