- const { EventEmitter } = require('events');
- const { stdin } = require('process');
- class Game extends EventEmitter {
- constructor(width, height, sprites) {
- super();
- this.width = width;
- this.height = height;
- this.sprites = sprites;
- this.goals = 0;
- this.okGoals = 0;
- this.on('levelEnd', this.nextLevel);
- this.on('move', this.onMove);
- };
- resetLines() {
- this.lines = [];
- for(let y = 0; y < this.height; y ++) {
- const line = new Array(this.width).fill('air');
- this.lines.push(line);
- };
- };
- nextLevel() {
- this.okGoals = 0;
- this.resetLines();
- this.addBorderWalls();
- const walls = 1 + Math.random() * 2;
- for(let i = 0; i < walls; i ++)
- this.spawn('wall');
- const boxes = 1 + Math.floor(Math.random() * 2);
- this.goals = boxes;
- for(let i = 0; i < boxes; i ++) {
- this.spawn('box', 2);
- this.spawn('goal');
- };
- this.spawn('player');
- this.update();
- };
- getPlayerPos() {
- const y = this.lines.findIndex((line) => line.includes('player'));
- const line = this.lines[y];
- if(!line) {
- this.spawn('player');
- return this.getPlayerPos();
- };
- const x = line.indexOf('player');
- return { x, y };
- };
- setItemPos(x, y, newX, newY) {
- const char = this.lines[y][x];
- this.lines[y][x] = 'air';
- this.lines[newY][newX] = char;
- };
- moveItem(x, y, side) {
- const sides = {
- left: [ 'x', -1 ],
- up: [ 'y', -1 ],
- down: [ 'y', 1 ],
- right: [ 'x', 1 ]
- };
- const [ coord, apply ] = sides[side];
- const newPos = { x, y };
- newPos[coord] += apply;
- const collide = this.lines[newPos.y][newPos.x];
- const char = this.lines[y][x];
- if(collide === 'box') {
- const ok = this.moveItem(newPos.x, newPos.y, side);
- if(!ok) return;
- } else {
- if(char === 'box' && collide === 'goal') {
- this.okGoals ++;
- if(this.okGoals >= this.goals)
- this.emit('levelEnd');
- this.lines[newPos.y][newPos.x] = 'air';
- return true;
- };
- if(collide !== 'air') return;
- };
- this.setItemPos(x, y, newPos.x, newPos.y);
- return true;
- };
- onMove(side) {
- const pos = this.getPlayerPos();
- this.moveItem(pos.x, pos.y, side);
- this.update();
- };
- spawn(item, offset = 1) {
- const randomPos = (max) => Math.floor(offset + Math.random() * (max - offset * 2));
- const x = randomPos(this.width);
- const y = randomPos(this.height);
- if(this.lines[y][x] === 'air') {
- this.lines[y][x] = item;
- } else {
- this.spawn(item, offset);
- };
- };
- addBorderWalls() {
- for(let x = 0; x < this.width; x ++) {
- this.lines[0][x] = 'wall';
- this.lines[this.height - 1][x] = 'wall';
- };
- for(let y = 0; y < this.height; y ++) {
- this.lines[y][0] = 'wall';
- this.lines[y][this.width - 1] = 'wall';
- };
- };
- update() {
- const frame = this.lines
- .map((line) => line.map((char) => this.sprites[char]).join(''))
- .join('\n');
- this.emit('update', frame);
- };
- };
- const game = new Game(16, 7, {
- player: '@',
- air: ' ',
- wall: '#',
- box: '%',
- goal: '$'
- });
- game.on('update', (state) => {
- console.clear();
- console.log(state);
- });
- game.nextLevel();
- stdin.setRawMode(true);
- stdin.setEncoding('utf-8')
- stdin.resume();
- const sides = {
- a: 'left',
- w: 'up',
- s: 'down',
- d: 'right'
- };
- stdin.on('data', (key) => {
- if(key === '\u0003') process.exit();
- const side = sides[key];
- if(side) game.emit('move', side);
- });