RCRPG/Javascript
< RCRPG
RCRPG/Javascript is part of RCRPG. You may find other members of RCRPG at Category:RCRPG.
Javascript version of RCRPG. The code can also be checked out and contributed to on github .
Code
const _ = require('lodash'),
readline = require('readline');
///////////////////
// LODASH IMPORT //
///////////////////
// import all lodash functions to the main namespace, but isNaN not to cause conflicts
_.each(_.keys(_), k => global[k === 'isNaN' ? '_isNaN' : k] = _[k]);
///////////////
// CONSTANTS //
///////////////
const DIRECTIONS = {
n: [ 0, -1, 0],
s: [ 0, 1, 0],
e: [-1, 0, 0],
w: [ 1, 0, 0],
u: [ 0, 0, -1],
d: [ 0, 0, 1]
};
/////////////
// HELPERS //
/////////////
function findDirection (world, dir, longName=false) {
const maybeDirection = find(world.commands, c => contains(first(c), dir)),
dirLetter = first(first(maybeDirection));
if (maybeDirection && has(DIRECTIONS, dirLetter)) {
return longName ? first(maybeDirection)[1] : dirLetter;
}
}
function makeRoom(location) {
return [location, null, compact([
random(0, 2) === 0 ? 'sledge': null,
random(0, 2) === 0 ? 'ladder': null,
random(0, 2) === 0 ? 'gold': null
])];
}
function listToDescriptiveString (lst) {
if (!lst) return;
if (lst.length === 1) return first(lst);
return dropRight(lst).join(', ') + ' and ' + last(lst);
}
function itemsToDescriptiveItems (items) {
return map(items, i => {
switch (i) {
case 'sledge': return 'a sledge';
case 'ladder': return 'a ladder';
case 'gold': return 'some gold';
}
});
}
//////////////
// MESSAGES //
//////////////
function help () {
return `You need a sledge to dig rooms and ladders to go upwards.
Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look.
Additionally you can tag rooms with the 'name' command and alias commands with 'alias'.
Have fun!
`;
}
function welcome () {
console.log(`Welcome to the dungeon!
Grab the sledge and make your way to room 1,1,5 for a non-existant prize!
`);
console.log(help());
}
/////////////
// ACTIONS //
/////////////
function gotoRoom (direction, world) {
const wantedRoom = zipWith(world.player.location, DIRECTIONS[direction], add),
room = find(world.rooms, r => isEqual(first(r), world.player.location));
if (direction === 'u' && !contains(room[2], 'ladder')) {
console.log("You can't go upwards without a ladder!");
} else {
if (find(world.rooms, r => isEqual(first(r), wantedRoom))) {
world.player.location = wantedRoom;
look(world);
} else {
console.log("There's no exit in that direction!");
}
}
return world
}
function dig (world, [direction]) {
let dir,
dirLongName,
wantedRoom;
if (!direction) {
console.log('Where do you want to dig?');
return world
}
if (!(dir = findDirection(world, direction))) {
console.log('That is not a direction I recognize');
return world;
}
if (!contains(world.player.inventory, 'sledge')) {
console.log('With your bare hands?');
return world;
}
dirLongName = findDirection(world, direction, true);
wantedRoom = zipWith(world.player.location, DIRECTIONS[dir], add);
if (find(world.rooms, r => isEqual(first(r), wantedRoom))) {
console.log('There is already an exit, there!');
return world;
}
world.rooms.push(makeRoom(wantedRoom));
console.log(`There is now an exit ${dirLongName}ward`);
return world;
}
function look (world) {
const room = find(world.rooms, r => isEqual(first(r), world.player.location)),
itemsOnFloor = itemsToDescriptiveItems(room[2]),
itemsOnFloorStr = isEmpty(itemsOnFloor)
? ''
: (itemsOnFloor.length === 1
? itemsOnFloor[0]
: listToDescriptiveString(itemsOnFloor)),
exits = chain(DIRECTIONS)
.map((d, kd) => [kd, zipWith(d, first(room), add)])
.filter(r => find(world.rooms, rr => isEqual(first(rr), r[1])))
.map(d => find(world.commands, c => {
return contains(first(c), first(d))
})[0][1])
.value(),
exitsStr = isEmpty(exits)
? ''
: (exits.length === 1
? `There is one exit: ${exits[0]}.`
: `You can see the following exits: ${listToDescriptiveString(exits)}.`);
console.log(
(room[1] ? room[1] : `Room at ${world.player.location[0]}, ${world.player.location[1]}, ${world.player.location[2]}`) + '\n' +
(!itemsOnFloorStr ? '' : `On the floor you can see ${itemsOnFloorStr}.\n`) +
exitsStr
);
return world;
}
function inventory (world) {
if (world.player.inventory.length === 0) {
console.log('You are not carrying anything');
} else {
console.log(`You are carrying: ${listToDescriptiveString(itemsToDescriptiveItems(world.player.inventory))}`);
}
return world;
}
function take (world, [item]) {
const room = find(world.rooms, r => isEqual(first(r), world.player.location))
if (!item) {
console.log('Take what?');
return world;
}
if (item === 'all') {
if (isEmpty(room[2])) {
console.log('There is nothing to take here');
} else{
world.player.inventory = uniq(world.player.inventory.concat(room[2] || []));
room[2] = [];
console.log('All items taken');
}
return world;
}
if (!contains(room[2], item)) {
console.log("You can't see anything like that here");
return world;
}
world.player.inventory = uniq(world.player.inventory.concat([item]));
pull(room[2], item);
console.log('Taken');
return world
}
function drop (world, [item]) {
const room = find(world.rooms, r => isEqual(first(r), world.player.location))
if (!item) {
console.log('Drop what?');
return world;
}
if (item === 'all') {
if (isEmpty(world.player.inventory)) {
console.log('You have nothing to drop');
} else{
room[2] = uniq(room[2].concat(world.player.inventory || []));
world.player.inventory = [];
console.log('All items dropped');
}
return world;
}
if (!contains(world.player.inventory, item)) {
console.log("You don't have that item");
return world;
}
room[2] = uniq(room[2].concat([item]));
pull(world.player.inventory, item);
console.log('Dropped');
return world
}
function equip (world, [item]) {
if (!item) {
console.log('What do you want to equip?');
return world;
}
if (!contains(world.player.inventory, item)) {
console.log("You don't have such object");
return world;
}
world.player.equipped = item;
console.log('Item equipped!');
return world;
}
function unequip (world, [item]) {
if (!item) {
console.log('What do you want to unequip?');
return world;
}
if (item !== world.player.equipped) {
console.log("You don't have it equipped");
return world;
}
world.player.equipped = null;
console.log('Item unequipped!');
return world;
}
function alias (world, [cmd, al]) {
let foundCommand;
if (!cmd && !al) {
console.log('Aliases:' + chain(world.commands)
.filter(c => first(c).length > 1)
.map(first)
.reduce((acc, c) => `${acc}\n${first(c)} => ${rest(c).join(', ')}`, '')
.value());
return world;
}
if (!(foundCommand = find(world.commands, c => contains(first(c), cmd)))) {
console.log('There is no such command');
return world
}
if (!al) {
let aliases = reject(first(foundCommand), c => c === cmd);
if (isEmpty(aliases)) {
console.log(`There are no aliases for ${cmd}`);
} else {
console.log(`Aliases for "${cmd}": ${aliases.join(', ')}`);
}
return world;
}
foundCommand[0] = uniq(first(foundCommand).concat([al]));
console.log('Alias assigned');
return world;
}
//////////////////////
// INPUT PROCESSING //
//////////////////////
function processInput(input, world) {
const splitInput = trim(input).split(/\s+/g),
[command, options] = [first(splitInput), rest(splitInput)],
commandFn = find(world.commands, c => contains(first(c), command.toLowerCase()));
if (commandFn) {
return commandFn[1](world, options);
} else {
console.log("I don't know what you mean.");
return world;
}
}
///////////////
// MAIN LOOP //
///////////////
(function runGame () {
let world = {
rooms: [
[[0, 0, 0], "The room where it all started...", ['ladder', 'sledge']],
[[1, 1 , 5], "You found it! Lots of gold!"]
],
commands: [
[['n', 'north'], partial(gotoRoom, 'n')],
[['s', 'south'], partial(gotoRoom, 's')],
[['w', 'west'], partial(gotoRoom, 'w')],
[['e', 'east'], partial(gotoRoom, 'e')],
[['d', 'down'], partial(gotoRoom, 'd')],
[['u', 'up'], partial(gotoRoom, 'u')],
[['help'], help],
[['dig'], dig],
[['l', 'look'], look],
[['i', 'inventory'], inventory],
[['take'], take],
[['drop'], drop],
[['equip'], equip],
[['unequip'], unequip],
[['alias'], alias]
],
player: {
location: [0, 0, 0],
inventory: ['sledge'],
equipped: null
}
};
welcome();
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', input => world = processInput(input, world))
})();