I've been working on a a text based game with a very simple battling system. There are way to many questions I can ask to get reviewed about this project, but I'll take small steps at a time (this is my first post).
I'm mostly concerned about how I handle command input and the execution of said commands within the game. Just to give a simple example of what a command might look like:
go north
go - command (called action in the code, as Command is the whole class)
north (and anything after that) - arguments, you most likely know about these
INPUT
Currently, I've made it work using the readline
package from Node. The input is being sent to a "commander" where it is parsed, made into a Command
which will in the end be passed to an "executor" which will execute the command. Here's the code:
//Player.ts
import readline from 'readline';
//other imports...
class Player extends Entity {
// fields, constructor and other methods...
async getInput(questionPrompt?: string): Promise<string> {
return new Promise((resolve, _) => {
const prompt = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (questionPrompt) ? questionPrompt : ">";
prompt.question(question, (answer) => {
prompt.close()
resolve(answer);
})
})
}
}
export default Player;
//commander.ts (technically it's inside commander/index.ts)
import Command from '../models/Command';
import Player from '../models/Player';
const actions = ['go', 'look', 'take', 'equip', 'inventory', 'help'];
export const parse = (text: string): (Command | null) => {
const action = text.split(' ')[0];
const args = text.split(' ').slice(1);
if (!actions.find((a) => action === a))
return null;
return new Command(action, args);
}
export const execute = (command: Command, receiver: Player) => {
switch (command.action) {
case 'go':
const success = receiver.move(command.args[0]);
if (success) {
return console.log(`You have entered a new room.\n${receiver.currentRoom.getDescription()}`);
} else {
return console.log(`You can't go there. Please try another one.`);
}
case 'look':
const text = `You are in ${receiver.currentRoom.name}.\n` +
`${receiver.currentRoom.description}`;
return console.log(text);
case 'take':
const item = receiver.currentRoom.takeItem(command.args[0]);
if (item)
return receiver.takeItem(item);
else
return console.log(`A ${command.args[0]} couldn't be found.`);
case 'equip':
return receiver.inventory.map((i) => {
if (i.name === command.args[0]) {
receiver.equip(i.id);
}
})
case 'inventory':
return console.log(receiver.inventory);
case 'help':
return console.log("No help for now... Maybe later?");
default:
console.log('Command error.');
return console.log(command);
}
}
export default {
parse,
execute,
}
I'm not entirely sure if this is an appropriate way to handle this use case, but this is all I could come up with. The other solution I managed to think of was to have the different commands in a separate folder where I could have them as modules and I could try to import them. This way they can maybe be classes and have their own help messages which will also make it easier to document the commands. The reason I decided to stick with this approach is because it seems a bit easier, and having the commands as separate modules kinds seems overkill to me, maybe I'm totally wrong.
I know I've probably missed something very important, so a link to the whole project can be found here.