UNPKG

@aige/core

Version:
442 lines (441 loc) 24.1 kB
#!/usr/bin/env node "use strict"; 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();