The Parser is Language Specific
Now sure if this is true in other platforms like Inform or TADS, but while designing Sharpee it became clear that the Parser makes a ton of assumptions about vocabulary and command structure that are directly tied to the language the author is using to tell their interactive story.
I already have a Language Provider package that hosts English messages and constants as 'lang-en-us'. It wasn't a huge leap to refactor the parser to be language specific as 'parser-en-us' and in its own package.
Then it was another very simple leap to how this works in a story. Here's an unimplemented sample piece of code that shows where this is going. Note this is using the standard library directly. We're still going to add another fluent layer so it's a simpler interface for authors.
The important part to highlight is the config object. You see language: 'en-us', which tells Sharpee to use the lang-en-us Language Provider package for messages and the parser-en-us Parser package for parsing player commands. If there are packages for lang-es-mx and parser-es-mx, then the author could configure language: 'es-mx' and create a Spanish language story.
// my-story.ts
import { Story, StoryConfig } from '@sharpee/engine';
import { WorldModel, IFEntity, EntityBuilder } from '@sharpee/world-model';
export class SpaceAdventureStory implements Story {
config: StoryConfig = {
id: 'space-adventure',
title: 'Space Station Mystery',
author: 'Jane Developer',
version: '1.0.0',
language: 'en-us', // Uses English parser and language
description: 'A mystery aboard a space station',
tags: ['sci-fi', 'mystery', 'puzzle']
};
initializeWorld(world: WorldModel): void {
// Create space station rooms
const bridge = new EntityBuilder('bridge')
.withName('Bridge')
.withDescription('The command center of the space station.')
.withTrait('if.trait.room')
.build();
const corridor = new EntityBuilder('corridor')
.withName('Main Corridor')
.withDescription('A long corridor with viewports showing stars.')
.withTrait('if.trait.room')
.build();
world.addEntity(bridge);
world.addEntity(corridor);
// Connect rooms
world.relate(bridge, 'if.rel.exit.south', corridor);
world.relate(corridor, 'if.rel.exit.north', bridge);
}
createPlayer(world: WorldModel): IFEntity {
const player = new EntityBuilder('player')
.withName('Commander Blake')
.withDescription('You are Commander Blake, investigating the mystery.')
.withTrait('if.trait.player')
.withTrait('if.trait.container')
.build();
// Start in the bridge
const bridge = world.getEntity('bridge');
if (bridge) {
world.relate(player, 'if.rel.within', bridge);
}
return player;
}
getCustomActions() {
// Story-specific actions
return [
{
id: 'space-adventure.scan',
patterns: ['scan', 'analyze'],
execute: (context) => {
// Custom scanning action
}
}
];
}
}
I'm still working through custom messages and actions and how they get registered, mainly so the eventual Text Service can query everything and report the current state properly.