- TypeScript 95.9%
- ANTLR 2.4%
- JavaScript 1.2%
- CSS 0.3%
- HTML 0.2%
| docs | ||
| lyre-js | ||
| lyrec | ||
| .gitignore | ||
| deno.jsonc | ||
| deno.lock | ||
| package-lock.json | ||
| README.md | ||
Lyre
Lyre is a narrative scripting language designed to build storylet games. A Lyre narrative is made up of a loose graph of individual storylets, atomic units of narrative. Each storylet offers branches, possible choices which lead to other storylets. As the player chooses branches and enters new storylets, their qualities (variables, essentially) change and that changes both which branches they see, and the outcomes of choosing branches.
What Lyre is and isn't
Lyre shares some structural similarity and design ideas with narrative scripting languages like Ink and Yarn, but it's designed for a much more narrow and specific use case.
Like Ink and Yarn, Lyre compiles to a JSON representation and is meant to run on a variety of contexts and runtimes (although only a Javascript runtime is available currently).
Lyre is designed as a spiritual descendant of StoryNexus, although with significant design differences.
Installation
Lyre has two components, the compiler (lyrec) and the runtime (lyre-js). Both are npm workspaces in this repository. Lyre is currently pre-alpha software, and is not available through npm's depot, but it can be installed via git.
$ npm install --global git+https://git.azhdarchid.space:bruno/lyre/lyrec
$ npm install git+https://git.azhdarchid.space:bruno/lyre/lyre-js
CLI Usage
You can compile a Lyre file with lyrec:
$ lyrec -o story.json story.lyre # Or use `npx lyrec` if not installed globally.
The output of the Lyre compiler is a Lyre story bundle, a JSON representation that the Lyre runtime uses.
The compiler also provides a barebones CLI interface for playing through Lyre files, which is intended for testing. You can access it through lyrec --run <filename>.
The Lyre runtime
Presently, Lyre only supplied a low-level runtime API which is designed to run anywhere, including in a server context where it's desirable for the runtime to be stateless. A minimal example:
import LyreRunner from "lyre-js";
import story from "./story.json" with { type: "json" };
const store = {}; // The quality store which holds the player's persistent state.
const frame = {}; // The frame is an object that holds the immediate content to display.
const runner = new LyreRunner(story);
frame = runner.begin("start");
console.log(frame);
See docs/api.md for the main methods supplied by the LyreRunner class.
Writing Lyre
A small example:
/* Lyre comments use C-like syntax. */
storylet anchor start
/** Every storylet has a unique identifier. This one is called `outside`;
* the "anchor" is the storylet the player goes back to by default; their
* current location, as it were. The first storylet in the game is set as
* the anchor initially by default. */
"Outside" // A string by itself is interpreted as the title.
effects
// Effects are the main way game state changes.
current_room = "outside"
+cloaked // This syntax means "set this boolean quality to true"
end
// `<<<` starts the text body of an entity.
<<<
We start our tale on... a dreary autumn evening, where fog is more
visible than buildings and rains quiets it all. Though it is unwise,
you stride confidently through the misty street, not once concerned about
the potential danger around you, or the pour soaking your <b>cloak</b>.
Bright light appears ahead, expanding as you approach the famed <b>Opera
House</b>. A chill runs down your spine. <i>Finally!</i>
>>>
branch
"Enter the Opera House"
-> foyer
end // Lyre isn't an indentation language; blocks like storylets and branches
// end with the `end` keyword.
branch
"Go Home"
-> ending_home
branch // However, you can "skewer" multiple blocks of the same type together.
"Examine your attire"
req (cloaked ?)
-> cloak
end
storylet cloak
"Taking Inventory"
/* Inside body blocks, {{ }} allows for string interpolation. <<< >>>
expressions can even be nested this way. */
<<<
{{ if current_room == "outside" then <<<
In this dreary weather, it is much too dark for you to see anything but the
blurry light of the Opera House ahead. Even looking down does not help
much, your feet even disappearing inside the mist. If you could not feel
the soft cloth against your skin, you would not even be able to tell that
you are currently wearing a cloak.
>>> else <<<
A delicate and expensive black velvet cloak, one you rarely take outside for
fear of ruining it. Yet, for some reason, you chose today of all days to
adorn it atop your shoulders.
Looking intently at the cloak, you think you see the last droplets of rain
running towards the ground, but you cannot tell for certain. The color of
the cloth is so deep and dark, you could swear it is sucking the light
surrounding it!
>>> }}
{{ !cloaked : "You can see the cloak hanging on the hook." }}
>>>
branch
"It may be best to return to your affairs..."
-> anchor
end
end