sheep-and-crocodile
Version:
Console game for nodejs about a sheep that runs away from a crocodile.
261 lines (236 loc) • 7.34 kB
JavaScript
var readline = require('readline'),
optimist = require('optimist'),
emoji = require('node-emoji'),
initArray = require('init-array'),
WorldmapGenerator = require('worldmap-generator');
require('colors');
if (optimist.argv.help) {
console.log(`Use arrows or WSAD buttons to control a sheep (${emoji.get('sheep')}).`);
console.log(`Your goal is eat as much clovers (${emoji.get('four_leaf_clover')}) as you can.`);
console.log(`Sheep eats clovers, crocodile (${emoji.get('crocodile')}) eats sheep. The crocodile will pursue the sheep until it is eaten by it.`);
console.log(`By the way there are some random water on the map. A crocodile can swim, but a sheep does not.`);
console.log(`You can exit game by Q button.`);
process.exit(0);
}
var gameIterationLength = 100;
switch(optimist.argv.difficulty) {
case 2: gameIterationLength = 90; break;
case 'nightmare':
case 3: gameIterationLength = 50; break;
default:
case 1: gameIterationLength = 110; break;
}
var waterEnabled = true;
if (optimist.argv.water !== undefined) {
waterEnabled = optimist.argv.water ? true : false;
}
var enableEmoji = true;
if (optimist.argv.emoji !== undefined) {
enableEmoji = optimist.argv.emoji ? true : false;
}
var sprites = {
player: enableEmoji ? emoji.get('sheep') : '@>'.white.bold,
enemy: enableEmoji ? emoji.get('crocodile') : 'X<'.black.bold,
grass: enableEmoji ? emoji.get('seedling') : 'vV'.grey.dim,
water: enableEmoji ? emoji.get('wavy_dash') : '~~'.cyan,
food: enableEmoji ? emoji.get('four_leaf_clover') : '~%'.bgYellow
}
const width = 20, height = 20;
var playerX, playerY;
var enemyX, enemyY;
var foodX, foodY;
var score;
var display;
var map;
function generateMap() {
map = initArray([width, height], 0);
var world = new WorldmapGenerator({
size: {
width: width,
height: height
},
tileTypes: [
{
name: 'grass',
connections: {'grass': 100, 'water': 1}
},
{
name: 'water',
connections: {'water': 15, 'grass': 1}
}
]
});
world.generate();
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
map[x][y] = world.map[x][y].name;
if (!waterEnabled && world.map[x][y].name == 'water') {
map[x][y] = 'grass';
}
}
}
}
function nextEnemyTurn() {
var turnX = 0,
turnY = 0;
if (enemyX < playerX) {
turnX = 1;
}
if (enemyY < playerY) {
turnY = 1;
}
if (enemyX > playerX) {
turnX = -1;
}
if (enemyY > playerY) {
turnY = -1;
}
if (Math.round(Math.random())) {
enemyX += turnX;
} else {
enemyY += turnY;
}
}
function placeNewFood() {
foodY = Math.round(Math.random() * (height - 1));
foodX = Math.round(Math.random() * (width - 1));
while (map[foodX][foodY] != 'grass') {
foodY = Math.round(Math.random() * (height - 1));
foodX = Math.round(Math.random() * (width - 1));
}
}
function processKeypress() {
if (!pressedKey) return;
var playerTurnY = 0;
var playerTurnX = 0;
switch (pressedKey.name) {
case 'w':
case 'up': playerTurnY--; break;
case 's':
case 'down': playerTurnY++; break;
case 'a':
case 'left': playerTurnX--; break;
case 'd':
case 'right': playerTurnX++; break;
}
if (playerY + playerTurnY < 0) {
playerY = 0;
}
if (playerX + playerTurnX < 0) {
playerX = 0;
}
if (playerY + playerTurnY >= height) {
playerY = height - 1;
}
if (playerX + playerTurnX >= width) {
playerX = width - 1;
}
if (map[playerX + playerTurnX] && map[playerX + playerTurnX][playerY + playerTurnY] == 'grass') {
playerX += playerTurnX;
playerY += playerTurnY;
}
pressedKey = null;
}
function initGame() {
generateMap();
placeNewFood();
playerX = Math.round(Math.random() * (width - 1));
playerY = Math.round(Math.random() * (height - 1));
while (map[playerX][playerY] != 'grass') {
playerX = Math.round(Math.random() * (width - 1));
playerY = Math.round(Math.random() * (height - 1));
}
enemyX = Math.round(Math.random() * (width - 1));
enemyY = Math.round(Math.random() * (height - 1));
score = 0;
display = initArray([width, height], sprites.grass);
display[playerX][playerY] = sprites.player;
display[foodX][foodY] = sprites.food;
}
function processScreen() {
nextEnemyTurn();
if (playerX == foodX && playerY == foodY) {
score++;
placeNewFood();
}
// map
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
switch(map[x][y]) {
case 'grass': display[x][y] = sprites.grass.bgGreen; break;
case 'water': display[x][y] = sprites.water.bgBlue; break;
}
}
}
// sheep
switch(map[playerX][playerY]) {
case 'grass': display[playerX][playerY] = sprites.player.bgGreen; break;
case 'water': display[playerX][playerY] = sprites.player.bgBlue; break;
}
// food
switch(map[foodX][foodY]) {
case 'grass': display[foodX][foodY] = sprites.food.bgGreen; break;
case 'water': display[foodX][foodY] = sprites.food.bgBlue; break;
}
// enemy
switch(map[enemyX][enemyY]) {
case 'grass': display[enemyX][enemyY] = sprites.enemy.bgGreen; break;
case 'water': display[enemyX][enemyY] = sprites.enemy.bgBlue; break;
}
if (playerX == enemyX && playerY == enemyY) {
display[enemyX][enemyY] = sprites.enemy.bgRed;
endGame('dead');
}
}
function renderScreen(noReturn = false) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write('Score: '.green + score.toString().green.bold);
for (let y = 0; y < height; y++) {
process.stdout.write('\n');
process.stdout.cursorTo(0);
for (let x = 0; x < width; x++) {
process.stdout.write(display[x][y]);
}
}
if (!noReturn) {
process.stdout.moveCursor(enableEmoji ? -width : -width*2, -height);
}
}
function endGame(reason = 'exit') {
renderScreen(true);
console.log('\n');
switch(reason) {
case 'dead':
console.log('YOU DIED.'.red.bold);
break;
case 'exit':
console.log('SUICIDE'.red.bold);
break;
}
console.log('Final score: '.green + score.toString().green.bold);
process.stdin.removeListener("keypress", keypress_handler);
process.exit(0);
}
// Keypress logger
var pressedKey = null;
readline.createInterface({
input: process.stdin,
output: process.stdout
});
readline.emitKeypressEvents(process.stdin);
var keypress_handler = function (str, key) {
pressedKey = key;
if (key.name == 'q') {
endGame('exit');
}
};
process.stdin.on('keypress', keypress_handler);
// Game cycle
initGame();
setInterval(() => {
processScreen();
renderScreen();
processKeypress();
}, gameIterationLength);