feat(tanks): tanks game!
This commit is contained in:
@ -74,4 +74,31 @@ export default class Interface {
|
||||
y: this.output.rows / 2
|
||||
}
|
||||
}
|
||||
|
||||
line(from, to) {
|
||||
let delta = {
|
||||
x: to.x - from.x,
|
||||
y: to.y - from.y
|
||||
}
|
||||
|
||||
let error = 0;
|
||||
|
||||
let deltaerr = Math.abs(delta.y / delta.x);
|
||||
|
||||
let { y } = from;
|
||||
|
||||
for (let x = from.x; x < to.x; x++) {
|
||||
this.cursor.goto(x, y);
|
||||
this.write('.');
|
||||
error += deltaerr;
|
||||
|
||||
while (error >= 0.5) {
|
||||
this.cursor.goto(x, y);
|
||||
this.write('.');
|
||||
y += Math.sign(delta.y);
|
||||
|
||||
error -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
127
src/classes/tank.js
Normal file
127
src/classes/tank.js
Normal file
@ -0,0 +1,127 @@
|
||||
import Unit from './unit';
|
||||
import fs from 'fs';
|
||||
|
||||
let log = (...strings) => {
|
||||
fs.appendFileSync(__dirname + '/../log', strings.join(' ') + '\n');
|
||||
}
|
||||
|
||||
const BULLET_SPEED = 10;
|
||||
|
||||
export default class Tank extends Unit {
|
||||
constructor(ui) {
|
||||
super(ui);
|
||||
|
||||
this._angle = 0;
|
||||
this.bullets = [];
|
||||
this.health = 100;
|
||||
|
||||
this.size = { x: 5, y: 3 };
|
||||
}
|
||||
|
||||
set angle(deg) {
|
||||
if (deg < 0) deg = 0;
|
||||
if (deg > 180) deg = 180;
|
||||
|
||||
this._angle = radian(deg);
|
||||
}
|
||||
|
||||
get angle() {
|
||||
return degree(this._angle);
|
||||
}
|
||||
|
||||
shoot() {
|
||||
let bullet = new Bullet(this.output);
|
||||
bullet.go(this.x + 2, this.y - 2);
|
||||
bullet.velocity = this.normalize();
|
||||
|
||||
log(this.angle, '(' + this._angle + ') =', bullet.velocity.join(','));
|
||||
|
||||
this.bullets.push(bullet);
|
||||
}
|
||||
|
||||
draw() {
|
||||
if (this.dead) return;
|
||||
|
||||
let { x, y } = this;
|
||||
if (this.color && this.color[0] === '#') {
|
||||
this.output.cursor.hex(this.color);
|
||||
} else if (this.color) {
|
||||
this.output.cursor[this.color]();
|
||||
}
|
||||
if (this.bold) this.output.cursor.bold();
|
||||
|
||||
|
||||
x = Math.round(x) + 2;
|
||||
y = Math.round(y) - 2;
|
||||
|
||||
let cannon;
|
||||
if (this.angle < 35) cannon = '_';
|
||||
else if (this.angle < 70) cannon = '/';
|
||||
else if (this.angle < 115) cannon = '|';
|
||||
else if (this.angle < 160) cannon = '\\';
|
||||
else cannon = '_';
|
||||
|
||||
this.output.cursor.goto(x + 2, y - 2);
|
||||
this.output.write(cannon);
|
||||
this.output.cursor.goto(x, y - 1);
|
||||
this.output.write('.===.');
|
||||
this.output.cursor.goto(x, y);
|
||||
this.output.write('o===o');
|
||||
this.output.cursor.reset();
|
||||
}
|
||||
|
||||
normalize() {
|
||||
return [Math.cos(this._angle), Math.sin(this._angle) * -1];
|
||||
}
|
||||
|
||||
static radian(deg) {
|
||||
return radian(deg);
|
||||
}
|
||||
|
||||
static degree(rad) {
|
||||
return degree(rad);
|
||||
}
|
||||
}
|
||||
|
||||
export class Bullet extends Unit {
|
||||
constructor(ui) {
|
||||
super(ui);
|
||||
|
||||
this.velocity = [0, 0];
|
||||
|
||||
this.shape = '•';
|
||||
this.color = 'red';
|
||||
|
||||
this.dieOnExit = true;
|
||||
|
||||
this.gravity = [0.8, 0.8];
|
||||
this.acceleration = [0, 0.015];
|
||||
}
|
||||
|
||||
speed() {
|
||||
this.velocity[0] += this.acceleration[0];
|
||||
this.velocity[1] += this.acceleration[1];
|
||||
|
||||
let x = this.velocity[0] * this.gravity[0];
|
||||
let y = this.velocity[1] * this.gravity[1];
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
collides(target) {
|
||||
let targetX = Math.round(target.x);
|
||||
let targetY = Math.round(target.y);
|
||||
let x = Math.round(this.x);
|
||||
let y = Math.round(this.y);
|
||||
|
||||
return x > targetX - target.size.x && x < targetX + target.size.x &&
|
||||
y > targetY - target.size.y && y < targetY + target.size.y;
|
||||
}
|
||||
}
|
||||
|
||||
function radian(deg) {
|
||||
return deg * Math.PI / 180;
|
||||
}
|
||||
|
||||
function degree(rad) {
|
||||
return rad * 180 / Math.PI;
|
||||
}
|
@ -8,7 +8,7 @@ export default class Unit {
|
||||
}
|
||||
|
||||
set health(value) {
|
||||
this._health = Math.min(0, value);
|
||||
this._health = Math.max(0, value);
|
||||
|
||||
if (this.health === 0) {
|
||||
this.dead = true;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Unit from './classes/unit';
|
||||
import Interface from './classes/interface';
|
||||
import fs from 'fs';
|
||||
|
||||
let FRAME = 100;
|
||||
let ui = new Interface();
|
||||
|
144
src/tanks.js
Normal file
144
src/tanks.js
Normal file
@ -0,0 +1,144 @@
|
||||
import Unit from './classes/unit';
|
||||
import Tank from './classes/tank';
|
||||
import Interface from './classes/interface';
|
||||
|
||||
let FRAME = 50;
|
||||
let ui = new Interface();
|
||||
let immoblize = false;
|
||||
|
||||
let one = new Tank(ui);
|
||||
one.go(10, ui.rows);
|
||||
|
||||
let two = new Tank(ui);
|
||||
two.go(ui.columns - 10, ui.rows);
|
||||
|
||||
let stop = false;
|
||||
|
||||
function loop() {
|
||||
if (stop) return;
|
||||
|
||||
ui.clear();
|
||||
|
||||
if (one.dead || two.dead) {
|
||||
let num = one.dead ? '2' : '1';
|
||||
const msg = `Player ${num} won!`;
|
||||
ui.cursor.red();
|
||||
ui.cursor.bold();
|
||||
|
||||
ui.cursor.goto(ui.center.x - msg.length / 2, ui.center.y);
|
||||
ui.write(msg);
|
||||
|
||||
stop = true;
|
||||
return;
|
||||
}
|
||||
|
||||
one.draw();
|
||||
one.bullets.forEach((bullet, i) => {
|
||||
if (bullet.dead) {
|
||||
immoblize = false;
|
||||
one.bullets.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
bullet.move();
|
||||
bullet.draw();
|
||||
|
||||
if (bullet.collides(two)) {
|
||||
two.health -= 10;
|
||||
bullet.dead = true;
|
||||
}
|
||||
});
|
||||
|
||||
ui.cursor.goto(0, 1);
|
||||
if (turn() === one) ui.cursor.hex('#54ffff');
|
||||
ui.write('Player 1');
|
||||
ui.cursor.reset();
|
||||
ui.cursor.goto(0, 2);
|
||||
ui.write('Health: ' + one.health);
|
||||
ui.cursor.goto(0, 3);
|
||||
ui.write('Angle: ' + parseInt(one.angle));
|
||||
|
||||
two.draw();
|
||||
two.bullets.forEach((bullet, i) => {
|
||||
if (bullet.dead) {
|
||||
immoblize = false;
|
||||
two.bullets.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
bullet.move();
|
||||
bullet.draw();
|
||||
|
||||
if (bullet.collides(one)) {
|
||||
one.health -= 10;
|
||||
bullet.dead = true;
|
||||
}
|
||||
});
|
||||
|
||||
ui.cursor.goto(ui.output.columns - 10, 1);
|
||||
if (turn() === two) ui.cursor.hex('#54ffff');
|
||||
ui.write('Player 2');
|
||||
ui.cursor.reset();
|
||||
ui.cursor.goto(ui.output.columns - 10, 2);
|
||||
ui.write('Health: ' + two.health);
|
||||
ui.cursor.goto(ui.output.columns - 10, 3);
|
||||
ui.write('Angle: ' + parseInt(two.angle));
|
||||
|
||||
setTimeout(loop, FRAME);
|
||||
}
|
||||
|
||||
loop();
|
||||
|
||||
ui.onKey('down', () => {
|
||||
if (immoblize) return;
|
||||
|
||||
turn().angle -= 1;
|
||||
})
|
||||
|
||||
ui.onKey('up', () => {
|
||||
if (immoblize) return;
|
||||
|
||||
turn().angle += 1;
|
||||
})
|
||||
|
||||
ui.onKey('left', () => {
|
||||
if (immoblize) return;
|
||||
|
||||
turn().x -= 1;
|
||||
});
|
||||
|
||||
ui.onKey('right', () => {
|
||||
if (immoblize) return;
|
||||
|
||||
turn().x += 1;
|
||||
});
|
||||
|
||||
ui.onKey('space', () => {
|
||||
if (immoblize) return;
|
||||
|
||||
turn().shoot();
|
||||
|
||||
immoblize = true;
|
||||
TURN = !TURN;
|
||||
});
|
||||
|
||||
ui.onKey(() => {
|
||||
if (one.dead || two.dead) {
|
||||
one.go(10, ui.rows);
|
||||
two.go(ui.columns - 10, ui.rows);
|
||||
|
||||
one.health = 100;
|
||||
two.health = 100;
|
||||
|
||||
one.dead = false;
|
||||
two.dead = false;
|
||||
|
||||
stop = false;
|
||||
|
||||
loop();
|
||||
}
|
||||
})
|
||||
|
||||
let TURN = true;
|
||||
function turn() {
|
||||
if (TURN) return one;
|
||||
return two;
|
||||
}
|
Reference in New Issue
Block a user