Advertisement
ForrestFox

Sokoban JS Main File

Mar 14th, 2021
414
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Класс для рисования на canvas
  2. class game {
  3.  
  4.     constructor() {
  5.  
  6.         this.el      = document.getElementById('viewport');
  7.         this.ctx     = this.el.getContext('2d');
  8.         this.width   = this.el.width;
  9.         this.height  = this.el.height;
  10.         this.sprite  = {};
  11.         this.map     = [];
  12.         this.info    = {};
  13.  
  14.         this.loading();
  15.     }
  16.  
  17.     // Загрузка ресурсов
  18.     loading() {
  19.  
  20.         let defs = {
  21.             brick:   "img/brick.png",
  22.             box:     "img/box.png",
  23.             mariolf: "img/mariolf.png",
  24.             mariort: "img/mariort.png",
  25.             place:   "img/place.png",
  26.             stone:   "img/stone.png",
  27.         };
  28.  
  29.         this.image_loading = 0;
  30.  
  31.         // Просмотреть все спрайты и загрузить
  32.         for (let n in defs) {
  33.  
  34.             // Увеличивается счетчик
  35.             this.image_loading++;
  36.  
  37.             // Создается новое изображение
  38.             let image = new Image();
  39.  
  40.             // Как только изображение загружено, уменьшим счетчик
  41.             image.onload = function() { this.image_loading--; }.bind(this);
  42.  
  43.             // Запрос изображения
  44.             image.src = defs[n];
  45.  
  46.             // Сохраним данные
  47.             this.sprite[n] = image;
  48.         }
  49.  
  50.         // Запуск игры
  51.         this.start();
  52.     }
  53.  
  54.     // Запуск игры
  55.     start() {
  56.  
  57.         // Ожидать, пока не будет загружены все ресурсы
  58.         if (this.image_loading) {
  59.  
  60.             // Вызвать `start` после 10 мс ожидания
  61.             setTimeout(function() { this.start() }.bind(this), 10);
  62.             return;
  63.         }
  64.  
  65.         // Рисовать первый уровень
  66.         this.setlevel(1);
  67.  
  68.         // Обработка нажатия на клавишу
  69.         window.onkeydown = function(e) {
  70.  
  71.             let k = e.key;
  72.  
  73.             // Предыдущее положение игрока (px, py)
  74.             let [dx, dy, movcnt, px, py] = [0, 0, 0, this.player.x, this.player.y];
  75.  
  76.             switch (k) {
  77.  
  78.                 case 'ArrowLeft':  dx = -1; dy =  0; this.player.dir = 'lf'; break;
  79.                 case 'ArrowRight': dx =  1; dy =  0; this.player.dir = 'rt'; break;
  80.                 case 'ArrowUp':    dx =  0; dy = -1; break;
  81.                 case 'ArrowDown':  dx =  0; dy =  1; break;
  82.                 default: return;
  83.             }
  84.  
  85.             // Есть возможность движения?
  86.             if (movcnt = this.doMove(dx, dy)) {
  87.  
  88.                 // Сдвинуть игрока с места
  89.                 this.player.x += dx;
  90.                 this.player.y += dy;
  91.                 this.info.steps++;
  92.  
  93.                 // Прорисовать впереди movcnt тайлов
  94.                 for (let i = 0; i <= movcnt; i++)
  95.                     this.drawTile(px + dx*i, py + dy*i);
  96.             }
  97.  
  98.             this.updateStat();
  99.  
  100.         }.bind(this)
  101.     }
  102.  
  103.     // Генерация уровня
  104.     setlevel(n) {
  105.  
  106.         switch (n) {
  107.  
  108.             case 1:
  109.  
  110.                 // 21x13
  111.                 this.map = [
  112.  
  113.                     ".....................",
  114.                     ".....#####...........",
  115.                     ".....#   #...........",
  116.                     ".....#*  #...........",
  117.                     "...###  *##..........",
  118.                     "...#  * * #..........",
  119.                     ".### # ## #...######.",
  120.                     ".#   # ## #####  xx#.",
  121.                     ".# *  *  @       xx#.",
  122.                     ".##### ### # ##  xx#.",
  123.                     ".....#     #########.",
  124.                     ".....#######.........",
  125.                     "....................."
  126.                 ];
  127.  
  128.                 break;
  129.         }
  130.  
  131.         // Информация об уровне
  132.         this.info = {
  133.             w: this.map[0].length,  // Кол-во символов в первой строке
  134.             h: this.map.length,     // Количество строк,
  135.             steps: 0,               // Кол-во шагов
  136.         };
  137.  
  138.         this.info.ox = (this.width  - this.info.w*32) / 2;
  139.         this.info.oy = (this.height - this.info.h*32) / 2;
  140.  
  141.         // Положение игрока
  142.         this.player = {x: -1, y: -1, dir: 'lf'};
  143.  
  144.         // Превращение в двухмерный массив
  145.         for (let i = 0; i < this.info.h; i++) this.map[i] = this.map[i].split("");
  146.  
  147.         // Поиск положения игрока
  148.         for (let y = 0; y < this.info.h; y++)
  149.         for (let x = 0; x < this.info.w; x++) {
  150.  
  151.             if (this.map[y][x] == '@') {
  152.                 this.player.x = x;
  153.                 this.player.y = y;
  154.                 this.map[y][x] = ' ';
  155.             }
  156.  
  157.             this.drawTile(x, y);
  158.         }
  159.  
  160.         // Пересчитать статистику
  161.         this.updateStat();
  162.     }
  163.  
  164.     // Проверка на возможность сдвинуться
  165.     doMove(dx, dy) {
  166.  
  167.         // Текущее положение игрока
  168.         let [px, py] = [this.player.x, this.player.y];
  169.  
  170.         // Проверить что впереди
  171.         let b0 = this.map[py + dy][px + dx];
  172.  
  173.         // Если впереди стена, то двинуться нельзя
  174.         if (b0 == '#') return 0;
  175.  
  176.         // Есть ящик, проверить возможности сдвинуть его
  177.         if (b0 == '*' || b0 == '%') {
  178.  
  179.             let b1 = this.map[py + 2*dy][px + 2*dx];
  180.  
  181.             // Впереди ящика стена или другой ящик, нельзя двинуться
  182.             if (['#','%','*'].includes(b1)) return 0;
  183.  
  184.             // Иначе сдвинуть ящик
  185.             this.map[py +   dy][px +   dx] = (b0 == '%' ? 'x' : ' ');
  186.             this.map[py + 2*dy][px + 2*dx] = (b1 == 'x' ? '%' : '*');
  187.  
  188.             return 2;
  189.         }
  190.  
  191.         return 1;
  192.     }
  193.  
  194.     // Обновление статистики
  195.     updateStat() {
  196.  
  197.         let estimate = 0, install = 0;
  198.  
  199.         for (let i = 0; i < this.info.h; i++)
  200.         for (let j = 0; j < this.info.w; j++) {
  201.  
  202.             switch (this.map[i][j]) {
  203.  
  204.                 case 'x': estimate++; break;
  205.                 case '%': install++; break;
  206.             }
  207.         }
  208.  
  209.         let text = 'Осталось: ' + estimate + ', установлено: ' + install + ', шагов: ' + this.info.steps;
  210.         if (estimate == 0) text = 'Вы прошли уровень';
  211.  
  212.         document.getElementById('stats').innerHTML = text;
  213.     }
  214.  
  215.     // Рисовать спрайт
  216.     draw(x, y, name) {
  217.        this.ctx.drawImage(this.sprite[name], this.info.ox + 32*x, this.info.oy + 32*y);
  218.     }
  219.  
  220.     // Нарисовать тайл из карты
  221.     drawTile(x, y) {
  222.  
  223.         let t = this.map[y][x];
  224.  
  225.         // Для fillRect
  226.         this.ctx.fillStyle = "#000";
  227.  
  228.         // Выбор картинки
  229.         switch (t) {
  230.             case '#': t = 'brick'; break;
  231.             case '.': t = 'stone'; break; // Кирпичи
  232.             case '*': t = 'box';   break; // Ящик
  233.             case '%': t = 'box';   break; // Ящик на полу
  234.             case 'x': t = 'place'; break;
  235.             default:  t = '';
  236.         }
  237.  
  238.         if (x == this.player.x && y == this.player.y) {
  239.             this.draw(x, y, 'mario' + this.player.dir); // Игрок
  240.         } else if (t) {
  241.             this.draw(x, y, t); // Спрайт
  242.         } else {
  243.             this.ctx.fillRect(this.info.ox + 32*x, this.info.oy + 32*y, 32, 32); // Пустота
  244.         }
  245.     }
  246. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement