@aige/core
Version:
AI Game Engine
425 lines (424 loc) • 22.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const prompts_1 = require("@inquirer/prompts");
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const _1 = require("./");
const package_json_1 = __importDefault(require("../package.json"));
const GameEvent_1 = __importDefault(require("./types/GameEvent"));
let chatMode = false;
commander_1.program
.name('aige')
.version(package_json_1.default.version)
.description(package_json_1.default.description)
.option('-i, --import <path>', 'Import a game')
.option('-h, --help', 'Display help information');
commander_1.program.parse();
const options = commander_1.program.opts();
commander_1.program.addHelpText('before', chalk_1.default.bgBlue.white('Run `aige` without arguments to start the CLI'));
if (options['help'])
commander_1.program.help();
const setupEventListeners = (game) => {
game.on(GameEvent_1.default.gain, ({ attribute, amount }) => console.log(chalk_1.default.green(`Gained ${amount} ${attribute}`)));
game.on(GameEvent_1.default.loss, ({ attribute, amount }) => console.log(chalk_1.default.red(`Lost ${Math.abs(amount)} ${attribute === 'money' ? game.data.money_name : attribute}`)));
game.on(GameEvent_1.default.quest_added, ({ quest }) => console.log(chalk_1.default.yellow(`New Quest: ${quest.name}`)));
game.on(GameEvent_1.default.quest_removed, ({ quest }) => console.log(chalk_1.default.green(`Quest removed: ${quest.name}`)));
game.on(GameEvent_1.default.character_added, ({ character }) => console.log(chalk_1.default.yellow(`New Character: ${character.name}`)));
game.on(GameEvent_1.default.character_removed, ({ character }) => console.log(chalk_1.default.green(`Character removed: ${character.name}`)));
game.on(GameEvent_1.default.inventory_added, ({ item }) => console.log(chalk_1.default.yellow(`New Item: ${item.name}`)));
game.on(GameEvent_1.default.inventory_removed, ({ item }) => console.log(chalk_1.default.green(`Item removed: ${item.name}`)));
game.on(GameEvent_1.default.ability_added, ({ ability }) => console.log(chalk_1.default.yellow(`New Ability: ${ability.name}`)));
game.on(GameEvent_1.default.ability_removed, ({ ability }) => console.log(chalk_1.default.green(`Ability removed: ${ability.name}`)));
game.on(GameEvent_1.default.inventory_added, ({ item }) => {
if (game.weightCarried + item.weight > game.data.weight_capacity)
console.log(chalk_1.default.red(`You are carrying too much weight!`));
});
const chatHandler = ({ chat, character, dialog }) => {
if (!chat || !character || !dialog || dialog === '')
return;
console.log(`${character.emoji || ''} ${chalk_1.default.green(character.name)}: ${chalk_1.default.yellow(dialog)}`);
chatMode = character.name;
};
game.on(GameEvent_1.default.chat, chatHandler);
};
const main = async () => {
let game = new _1.Game();
console.log(chalk_1.default.green(`AIGE v${package_json_1.default.version}`));
console.log(chalk_1.default.red('https://aige.games'));
console.log(chalk_1.default.blue('Type "help" for a list of commands'));
const gameCommands = [
{
name: 'help',
description: 'Display help information',
execute: () => {
console.log(chalk_1.default.green('Commands:'));
for (const command of gameCommands.sort((a, b) => a.name.localeCompare(b.name))) {
console.log(chalk_1.default.blue(` ${command.name}: ${command.description}`));
}
}
},
{
name: 'create',
description: 'Create a new game',
execute: async () => {
let language = await (0, prompts_1.input)({ message: 'Language:', default: 'en' });
let universe = await (0, prompts_1.input)({ message: 'Universe:', default: 'random' });
let playerName = await (0, prompts_1.input)({ message: 'Name:', default: 'random' });
let playerClass = await (0, prompts_1.input)({ message: 'Class:', default: 'random' });
if (universe === 'random')
universe = undefined;
if (playerName === 'random')
playerName = undefined;
if (playerClass === 'random')
playerClass = undefined;
game = new _1.Game({ language, universe, playerName, playerClass });
setupEventListeners(game);
console.log(chalk_1.default.green(`Creating game ${game.id}`));
await game.init();
console.log(chalk_1.default.green(`\n${game?.options.universe}`));
console.log(`${chalk_1.default.green(`${game?.options.playerName}, ${game?.options.playerClass}`)}`);
console.log(`Location: ${game?.data.location} (${game?.data.location_description})`);
console.log(`Weather: ${game?.data.weather} (${game?.data.weather_description})`);
console.log('Inventory: ' + game?.data.inventory?.map(item => item.name).join(', '));
console.log('Characters: ' + game?.data.characters?.map(character => character.name).join(', '));
console.log('Quests: ' + game?.data.quests?.map(quest => quest.name).join(', '));
console.log('Abilities: ' + game?.data.abilities?.map(ability => ability.name).join(', '));
console.log(`\n${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.yellow(`Suggested actions: ${game?.data.actions?.join(', ')}`)}`);
}
},
{
name: 'stats',
description: 'Display the player stats',
execute: () => {
console.log(`${chalk_1.default.green(`${game.options.playerName}, ${game.options.playerClass}`)}`);
console.log(`⭐\tLevel: ${game.level} (${game.data.experience} XP)`);
console.log(`❤️\tHealth: ${game.data.health} (${game.data.health_description})`);
console.log(`🛡️\tArmor: ${game.data.armor}%`);
console.log(`💰\tMoney: ${game.data.money} ${game.data.money_name}`);
console.log(`👍\tReputation: ${game.data.reputation} (${game.data.reputation_description})`);
console.log(`🏋️\tWeight Carried: ${game.overburdened ? chalk_1.default.red(game.weightCarried) : chalk_1.default.green(game.weightCarried)} / ${game.data.weight_capacity} ${game.data.weight_unit}`);
console.log(`🎒\tInventory: ${game.data.inventory?.map(item => item.name).join(', ')}`);
console.log('👥\tCharacters: ' + game.data.characters?.map(character => character.name).join(', '));
console.log('📜\tQuests: ' + game.data.quests?.map(quest => quest.name).join(', '));
console.log('🧠\tAbilities: ' + game.data.abilities?.map(ability => ability.name).join(', '));
}
},
{
name: 'images',
description: 'Generate images',
execute: async () => {
const types = await (0, prompts_1.checkbox)({
message: 'Types:',
choices: ['player', 'scene', 'character', 'item', 'ability'].map(type => ({ title: type, value: type }))
});
const args = {};
for (const type of types) {
switch (type) {
case 'player':
args.player = true;
break;
case 'scene':
args.scene = true;
break;
case 'character':
const character = await (0, prompts_1.select)({
message: 'Character:',
choices: game?.data.characters?.map(character => ({ title: character.name, value: character.name })) || []
});
if (character)
args.character = character;
break;
case 'item':
const item = await (0, prompts_1.select)({
message: 'Item:',
choices: game?.data.inventory?.map(item => ({ title: item.name, value: item.name })) || []
});
if (item)
args.item = item;
break;
case 'ability':
const ability = await (0, prompts_1.select)({
message: 'Ability:',
choices: game?.data.abilities?.map(ability => ({ title: ability.name, value: ability.name })) || []
});
if (ability)
args.ability = ability;
break;
}
}
if (Object.keys(args).length > 0) {
const results = await game?.images(args);
console.log(chalk_1.default.green(`Generated ${Object.keys(results).length} images`));
for (const [key, value] of Object.entries(results)) {
console.log(`${chalk_1.default.yellow(key)}: ${chalk_1.default.blue(value.url)}`);
}
}
}
},
{
name: 'inspect',
description: 'Inspect the current game (or a specific top-level key)',
execute: (args) => {
const key = args?.[1];
switch (key) {
case 'inventory':
case 'characters':
case 'abilities':
case 'quests':
console.dir(game?.data[key], { depth: null });
break;
case 'weather':
console.log(`Weather: ${game?.data.weather} (${game?.data.weather_description})`);
break;
case 'location':
console.log(`Location: ${game?.data.location} (${game?.data.location_description})`);
break;
case 'appearance':
console.log(`Appearance: ${game?.data.appearance}`);
break;
case 'rumor':
console.log(`Rumor: ${game?.data.rumor}`);
break;
case 'tokens':
console.log(`Tokens Used: ${game?.client.tokens}`);
break;
default:
try {
if (key)
console.dir(game?.data[key] || game?.data, { depth: null });
else
console.log(game.inspect());
}
catch (err) {
console.log(game.inspect());
}
}
}
},
{
name: 'tokens',
description: 'Display the number of tokens used',
execute: () => console.log(`Tokens Used: ${game?.client.tokens}`)
},
{
name: 'action',
description: 'Perform an action on the current game',
execute: async (args) => {
const actions = game?.data.actions || ['Look around'];
let action = args?.join(' ');
if (!action || action === 'action') {
action = await (0, prompts_1.select)({
message: 'Action:',
choices: actions.map(action => ({ title: action, value: action, description: '' }))
.concat([{ title: 'Other', value: 'Other', description: 'Specify a custom action' }, { title: 'Cancel', value: 'Cancel', description: 'Do not perform any action' }])
});
}
if (action === 'Cancel')
return;
if (action === 'Other')
action = await (0, prompts_1.input)({ message: 'Action:' });
await game?.action(action);
console.log(`\n${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.green(`Suggested actions: ${chalk_1.default.yellow(game?.data.actions?.join(', '))}`)}`);
}
},
{
name: 'set',
description: 'Set a game value',
execute: async (args) => {
const key = args && args.length > 1 ? args[1] : await (0, prompts_1.input)({ message: 'Key:' });
if (!key || key === '')
return;
const value = args && args.length > 2 ? args[2] : await (0, prompts_1.input)({ message: 'Value:' });
game?.set(key, value);
console.log(chalk_1.default.green(`Set ${key} to ${value}`));
}
},
{
name: 'scene',
description: 'Display the current scene',
execute: () => {
console.log(`${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`Location: ${game?.data.location} (${game?.data.location_description})`);
console.log(`Weather: ${game?.data.weather} (${game?.data.weather_description})`);
}
},
{
name: 'inventory',
description: 'Use an inventory item',
execute: async () => {
const item = await (0, prompts_1.select)({
message: 'Item:',
choices: game?.data.inventory?.map(item => {
const title = `${item.emoji || ''} ${item.name} (${item.type})`;
const value = item.name;
const description = `${item.description} (${item.value} ${game?.data.money_name}, ${item.weight} ${game?.data.weight_unit})`;
return { title, value, description };
}).concat([{ title: 'Cancel', value: 'Cancel', description: 'Do not use an item' }])
});
if (item === 'Cancel')
return;
console.log(chalk_1.default.yellow(`Using ${item}`));
await game?.action(`Use ${item}`);
console.log(`\n${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.yellow(`Suggested actions: ${game?.data.actions?.join(', ')}`)}`);
}
},
{
name: 'quests',
description: 'Display the current quests',
execute: async () => {
const quests = game?.data.quests?.map(quest => {
const title = quest.name;
const value = quest.name;
const description = `${quest.emoji || ''} ${quest.description}`;
return { title, value, description };
}) || [];
if (quests.length === 0)
return console.log(chalk_1.default.yellow('No quests'));
const quest = await (0, prompts_1.select)({
message: 'Quest:',
choices: quests.concat([{ title: 'Cancel', value: 'Cancel', description: 'Do not choose a quest' }])
});
if (quest === 'Cancel')
return;
console.log(chalk_1.default.yellow(`Continuing quest ${quest}`));
await game?.action(`${game.options.prompts?.quest}: ${quest}`);
console.log(`\n${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.yellow(`Suggested actions: ${game?.data.actions?.join(', ')}`)}`);
}
},
{
name: 'talk',
description: 'Talk to a character',
execute: async () => {
const characters = game?.data.characters?.map(character => {
const title = character.name;
const value = character.name;
const description = `${character.emoji || ''} ${character.description}`;
return { title, value, description };
}) || [];
if (characters.length === 0)
return console.log(chalk_1.default.yellow('No characters'));
const name = await (0, prompts_1.select)({
message: 'Character:',
choices: characters.concat([{ title: 'Cancel', value: 'Cancel', description: 'Do not choose a character' }])
});
if (name === 'Cancel')
return;
console.log(chalk_1.default.yellow(`Talking to ${name}`));
const character = game?.data.characters?.find(character => character.name === name);
if (!character)
return console.log(chalk_1.default.red('Invalid character'));
await game?.chat({ character, dialog: '*start new conversation*' });
chatMode = character.name;
}
},
{
name: 'abilities',
description: 'Display the current abilities of the player',
execute: async () => {
const abilities = game?.data.abilities?.map(ability => {
const title = ability.name;
const value = ability.name;
const description = `${ability.emoji || ''} ${ability.description}`;
return { title, value, description };
}) || [];
if (abilities.length === 0)
return console.log(chalk_1.default.yellow('No abilities'));
const ability = await (0, prompts_1.select)({
message: 'Ability:',
choices: abilities.concat([{ title: 'Cancel', value: 'Cancel', description: 'Do not choose an ability' }])
});
if (ability === 'Cancel')
return;
console.log(chalk_1.default.yellow(`Using ability ${ability}`));
await game?.action(`Use ability: ${ability}`);
console.log(`\n${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.yellow(`Suggested actions: ${game?.data.actions?.join(', ')}`)}`);
}
},
{
name: 'export',
description: 'Export the current game',
execute: async () => {
const savePath = await (0, prompts_1.input)({ message: 'Export to file:' });
if (!savePath || savePath === '')
return console.log(chalk_1.default.red('Invalid path'));
fs_1.default.writeFileSync(savePath, JSON.stringify(game?.export(), null, 2));
console.log(chalk_1.default.green(`Game ${game?.id} exported`));
}
},
{
name: 'import',
description: 'Import a game',
execute: async () => {
const loadPath = await (0, prompts_1.input)({ message: 'Import from path:' });
const data = JSON.parse(fs_1.default.readFileSync(loadPath).toString());
game = new _1.Game();
game.import(data);
setupEventListeners(game);
console.log(chalk_1.default.green(`Game ${game.id} imported`));
console.log(chalk_1.default.green(`\n${game?.options.universe}`));
console.log(`${chalk_1.default.green(`${game?.options.playerName}, ${game?.options.playerClass}`)}`);
console.log(`${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.yellow(`Suggested actions: ${game?.data.actions?.join(', ')}`)}`);
}
}
];
if (options['import']) {
const data = JSON.parse(fs_1.default.readFileSync(options['import']).toString());
game = new _1.Game();
game.import(data);
setupEventListeners(game);
console.log(chalk_1.default.green(`Game ${game.id} imported`));
console.log(chalk_1.default.green(`\n${game?.options.universe}`));
console.log(`${chalk_1.default.green(`${game?.options.playerName}, ${game?.options.playerClass}`)}`);
console.log(`${game?.data.scene_emoji || ''}\t${chalk_1.default.blue(game?.data.scene)}`);
console.log(`\n${chalk_1.default.yellow(`Suggested actions: ${game?.data.actions?.join(', ')}`)}`);
}
while (true) {
if (chatMode) {
const character = game.data.characters?.find(character => character.name === chatMode);
if (!character) {
chatMode = false;
continue;
}
const response = await (0, prompts_1.input)({ message: '💬' });
if (response !== '')
await game.chat({ character, dialog: response });
else
chatMode = false;
continue;
}
let command = await (0, prompts_1.input)({ message: '🎮' });
if (['quit', 'exit', 'break'].includes(command))
break;
if (command === '')
command = 'action';
try {
const args = command.split(' ');
let cmd = gameCommands.find(cmd => cmd.name === args?.[0]?.trim());
if (args.length > 1 && !['inspect', 'set'].includes(args?.[0] || ''))
cmd = undefined;
if (cmd)
await cmd.execute(args);
else {
const cmd = gameCommands.find(cmd => cmd.name === 'action');
const args = command.split(' ');
if (cmd)
await cmd.execute(args);
}
}
catch (err) {
console.log(chalk_1.default.red(err));
}
}
};
main();