@aige/core
Version:
AI Game Engine
442 lines (441 loc) • 24.1 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 emojify_1 = require("@twuni/emojify");
const chalk_1 = __importDefault(require("chalk"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const _1 = require("./");
const types_1 = require("./types");
let chatMode = false;
let pkg = { version: '?.?.?', description: 'AI Game Engine' };
try {
pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, 'pkg.json')).toString());
}
catch (err) { }
commander_1.program
.name('aige')
.version(pkg.version)
.description(pkg.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(types_1.GameEvent.gain, ({ attribute, amount }) => console.log(chalk_1.default.green(`Gained ${amount} ${attribute}`)));
game.on(types_1.GameEvent.loss, ({ attribute, amount }) => console.log(chalk_1.default.red(`Lost ${Math.abs(amount)} ${attribute === 'money' ? game.data.money_name : attribute}`)));
game.on(types_1.GameEvent.quest_added, ({ quest }) => console.log(chalk_1.default.yellow(`New Quest: ${quest.name}`)));
game.on(types_1.GameEvent.quest_removed, ({ quest }) => console.log(chalk_1.default.green(`Quest removed: ${quest.name}`)));
game.on(types_1.GameEvent.character_added, ({ character }) => console.log(chalk_1.default.yellow(`New Character: ${character.name}`)));
game.on(types_1.GameEvent.character_removed, ({ character }) => console.log(chalk_1.default.green(`Character removed: ${character.name}`)));
game.on(types_1.GameEvent.inventory_added, ({ item }) => console.log(chalk_1.default.yellow(`New Item: ${item.name}`)));
game.on(types_1.GameEvent.inventory_removed, ({ item }) => console.log(chalk_1.default.green(`Item removed: ${item.name}`)));
game.on(types_1.GameEvent.ability_added, ({ ability }) => console.log(chalk_1.default.yellow(`New Ability: ${ability.name}`)));
game.on(types_1.GameEvent.ability_removed, ({ ability }) => console.log(chalk_1.default.green(`Ability removed: ${ability.name}`)));
game.on(types_1.GameEvent.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(`${(0, emojify_1.emojify)(character.emoji || '')} ${chalk_1.default.green(character.name)}: ${chalk_1.default.yellow(dialog)}`);
chatMode = character.name;
};
game.on(types_1.GameEvent.chat, chatHandler);
};
const main = async () => {
let game = new _1.Game();
console.log(chalk_1.default.green(`AIGE v${pkg.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' });
let tokenLimit = await (0, prompts_1.input)({ message: 'Token Limit:', default: 'None' });
if (universe === 'random')
universe = undefined;
if (playerName === 'random')
playerName = undefined;
if (playerClass === 'random')
playerClass = undefined;
if (tokenLimit === 'None')
tokenLimit = undefined;
game = new _1.Game({ language, universe, playerName, playerClass, clientOptions: { limit: tokenLimit ? parseInt(tokenLimit) : Infinity } });
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${(0, emojify_1.emojify)(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(`🪞\tAppearance: ${game.data.appearance}`);
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: ['cover', 'player', 'scene', 'character', 'item', 'ability'].map(type => ({ title: type, value: type }))
});
const args = {};
let imageOptions = {};
for (const type of types) {
switch (type) {
case 'cover':
args.cover = true;
imageOptions.cover = { model: 'dall-e-3', quality: 'hd', size: '1792x1024' };
break;
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 = game?.data.characters?.find(char => char.name === 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, imageOptions);
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 key/path)',
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${(0, emojify_1.emojify)(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(`${(0, emojify_1.emojify)(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 = `${(0, emojify_1.emojify)(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${(0, emojify_1.emojify)(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 = `${(0, emojify_1.emojify)(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${(0, emojify_1.emojify)(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 = `${(0, emojify_1.emojify)(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}; enter an empty message to stop`));
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 = `${(0, emojify_1.emojify)(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${(0, emojify_1.emojify)(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(`${(0, emojify_1.emojify)(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(`${(0, emojify_1.emojify)(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 === '' && game.data.actions)
command = 'action';
if (command === '' && !game.data.actions)
command = 'create';
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();