pjohansson / inkling

Limited Rust implementation of the Ink markup/scripting language for game narratives
Other
42 stars 7 forks source link

Story generation #8

Open Kinrany opened 3 years ago

Kinrany commented 3 years ago

Hi! I'm trying to convert stories written in another tool into stories written in ink.

I wondered if I could use your crate. I assume it is not intended for this purpose, but I wonder how hard it would be to change that.


This is not exactly the feedback you asked for, but I have an idea of what the high-level API could look like and I thought you might want to hear it:

  1. Source code representation struct Source: represents an .ink text file. Implements From<&str> for parsing and Display for printing. Allows errors.
  2. High-level story representation struct Story: has all the content of the story, but without the details. Implements TryFrom<Source> and Into<Source>. Does not allow errors. Can be used to start playing a story.
  3. Runtime representation enum Playthrough { Text, Choice }: tracks the current state of the story being played. Stops at either a text line or a choice. Basically a state machine.

The implementation of the runtime representation is a lower level detail than the overview and less important. I'll just present the API that I think seems the most obvious (after thinking about it for like 20 minutes) for embedding the crate as a story engine in another program.

The most common flow would look like this:

// read the text
let source: Source = "hello world".into();
// compile the story
let story: Story = text.try_into()?;
// create an active instance of the story
let mut playthrough = story.start();

loop {
    match playthrough {
        Text { line, next } => {
            // show the paragraph and
            // proceed to the next step
            println!("{}", line);
            playthrough = next;   
        },
        Choice(choice) => {
            // show the options, let the reader choose one,
            // pass the chosen option to the state machine,
            // proceed to the step determined by the chosen option
            println!("{}", choice.options);
            playthrough = choice.choose(get_chosen_option());
        },
        End => {
            println!("FIN");
            break;
        }
    }
}

This would neatly split the implementation into three smaller reusable parts that would allow a range of different. It would allow using the crate for editing ink files and for publishing finished stories in various formats.

Any or all of this might be overengineering though 🙂

pjohansson commented 2 years ago

Hi!

Sorry for the late reply, My actual work keeps most of my attention these days.

  1. You are of course welcome to use inkling in any way you like, including converting stories from other tools if you want to -- as long as you follow the Parity license terms for this project if it is used.
  2. Thank you for your thoughts on the API. While I haven't actively worked on the code over the past year, I've largely landed on something similar to your suggestion about a Playthrough struct, or something similar to it. It would allow a more granular structure, like explicitly calling attention to when new story sections (knots) are jumped to, not just when choices are hit.
  3. I'm not sure what a Source struct would add if its mostly wrapped text content. I could see the use of separately parsing text and checking for errors, and creating the Story from that object. But in the end, the same errors would be raised, no? But I'll admit that I'm an amateur who does not have any real experience with dealing with these sorts of issues in real-life contexts.

Regards, Petter

Kinrany commented 2 years ago

Thanks for the reply!

I have the same problem. Perhaps another half a year later I'll find the time to revisit that project and send a PR, haha