Tiny QBN for Twine/Sugarcube

For the last year or so I’ve been working on a mostly-text game in LOVE2D with a couple of friends (a silly space adventure inspired by Oregon Trail and Seedship) with a couple friends. We’ve been using a filtered-card-deck system where events require certain true/false flags to be selectable (are we in a particular type of solar system? Are our food resources getting low? etc.). It’s extremely simple but surprisingly capable.

Since I saw the announcement about StoryNexus closing their doors to new worlds I’ve been mulling over the idea of creating some sort of tool to help fill that seemingly-under-served niche…and today I sat down and banged out some JavaScript for Twine/Sugarcube to implement a system like the one we’ve been using.

So you tag passages with requirements (“req-lowFuel”, “req-not-lowMoney”), insert matching passages (all or a random selection) wherever you want, and continue to use links as usual in Twine where that’s appropriate. There’s a helper function to generate the appropriate range flags (low/medium/high or whatever) from variables

Does that sound like something that there’s any chance people would be interested in? I’ll probably keep playing with it either way but I’ll put more effort into docs and a tutorial story if other people want to play along.

–Josh

2 Likes

Yes, please! I’m interested in seeing this.

I am also interested in seeing and experimenting with your work as it progresses.

Thank you.

Wow, two positive responses! I was expecting deafening silence. :slight_smile:

Here’s what I have so far: github.com/JoshuaGrams/tiny-qbn

I think it’s feature-complete relative to what we’re using in our Love2d game.

I wrote 700-ish words of reference documentation which I think describes everything, but it’s fairly dry. I’m working on hammering out a small demo that shows it in action. And it definitely needs a “cookbook” showing some of the more involved techniques.

Also, it all basically seems to work OK, but I’m still working through my list of corner-cases and writing tests for them, so who knows?

I ain’t dead, I’se just procrastinatin’…

I spent three days waffling around because I don’t know how to write fiction so I kept getting bogged down on what to write, and because I find Twine so irritating to use. And then I got hopelessly sidetracked by implementing about half the puzzles from Mike Spivey’s Junior Arithmancer, but I guess that was a good test of doing more complex things with it.

But I’m getting back on track.

  • I found and fixed a bunch of bugs.
  • I moved the styling out of the Javascript and into Sugarcube widgets, which is more wordy to use but also more flexible.
  • I made things interact properly with Sugarcube’s history.
  • I think I figured out a demo story snippet that I can actually write: hopefully I can finish that this weekend.

I’m still experimenting with ways to do StoryNexus-style “show the requirements for this story” thing, and to deal with stories that should be visible even though not all their requirements are met (so you know what you’re working toward). Those things are possible already, but you have to do it yourself and it seems a bit clunky and brittle. I’m having trouble deciding whether to just document the techniques and idioms or whether I can figure out some helper macros that make it easier without restricting the possibilities too much. As with the styling, I’d like to push as much as possible into Twine code so it’s more flexible, but some things are better (or only possible) in Javascript.

I don’t know if this helps you or not, but AXMA Story Maker’s latest version is all in JavaScript. I’m stumped on it, but I’ve always preferred ASM to Twine. Still waiting for the full English documentation - right now I’m reading the Russian doc online and auto Google-translating it which is sometimes not very helpful…

Make sure translation is on to read the site (unless you know Russian) - There is an English part of the board.

axma.info/

Huh. I had looked at AXMA a while back, but when I tried using the Google translate web-page on the manual under Firefox, it didn’t work: it just translated the base page and not the actual Javascript-presented manual pages. I’ll have to try Chrome’s built-in Google-translate…

Yeah, that works. Thanks.

Edit: I read the manual and played around with the tool for a while. It is nicer than Twine in a bunch of ways, but I have a thing about not directly donating my time and energy to support proprietary products…

OK, I think that I’ve worked out all the major kinks and am done “improving” the API. I’m really happy with this, especially for something I built in about 10 days of my spare time.

I made a three-part tutorial walking through the creation of a simple example. I should really add some more content to the last stage of the example, but I think it’s enough to get started with. I made videos of the tutorials and posted them to Youtube. They’re probably terrible, but if you prefer video to text, they exist. Though if you prefer video to text, what are you doing here? :wink:

github.com/JoshuaGrams/tiny-qbn … d-examples

If anybody takes a look at this, I’m open to suggestions of where to go next with the documentation. Or feature requests: I’ve built three half-baked test pieces (plus my experience with the Love2D game) but it’s always possible (even likely?) that there are things I haven’t anticipated. Twine puts some significant limitations on things, but if you have needs that this doesn’t meet, I’ll see if I can reasonably do anything about it.

–Josh

Well. I started trying to implement some StoryNexus inspired convenience functions, ran smack into SugarCube’s guard rails, and set it aside for a couple days to think about how to redesign things to get them to work within the constraints. Aaaand then got into a slump for several months where I didn’t accomplish much on any of my projects. Bleh.

But I’m going to NarraScope (in about a week! it’s coming up so fast!) and that has me thinking about IF, so I’m putting some time into this again. I picked up my four failed scribbled draft redesigns and put the pieces together into something that should be reasonably comfortable to use. I got the hard part (I think) into a working-but-mostly-untested state. So I can now present a menu of choices that are filtered like little cards by writing something like:

<<choices 'someChoices'>>
    <<when>>req-somewhat_uncoordinated<<offer>>blah blah blah
    <<when>>sticky-card req-very_charming<<offer>>other thing
<</choices>>

And I worked around the issue with not being able to store actual passage objects in SugarCube variables (they get destroyed when you go to the next turn), and made a macro <<fillhand $hand handsize passages>> to help maintain a persistent hand of cards that the user can choose from.

And you can wrap a whole card in a <<card>>cover<<contents>>...<</card>> to have “two-sided” cards, and extra requirements (tagged with also-...) for whether the contents should be available. Though AFAICS there’s no way for me to detect whether a card is single- or double-sided, so the author is responsible for using only one or the other, or somehow making sure they don’t get mixed. Oh well.

I still have to make some sort of conditional link macro for difficulty requirements (“A modest challenge”, “A very chancy challenge” and the like). I’m not sure how much of a pain that will be.

I also haven’t yet started to design a good interface for displaying requirements to the player. Presumably you want to be able to have images/icons for at least some of the requirements. Or maybe filter them to display some requirements in a different place than others? That’s next on my list. I think some of the techniques I’m already using will make the JavaScript/SugarCube side of things pretty straightforward. So it’s just a naming/interface-design thing.

Anyway. I’m hoping to get the rest of the code drafted before I have to break for NarraScope, but it will easily take me the rest of the month to get everything reasonably tested and documented, and to go over the StoryNexus reference doc again to make sure I’m not missing anything major. But it feels good to have figured out how to make some of this work, so I wanted to post about it. I’m pushing the code to a branch on GitHub (there should be a “branch” pulldown somewhere).

1 Like

I’ve made reasonable progress the last three days…

Friday, June 7

I spent a little time thinking about how to implement difficulty
checks (should be trivial) and displaying requirements (not so
much).

But the big thing I did was to go through the entire StoryNexus
Reference Guide and summarize it. It’s amazing how much of it is
about different ways to display or check qualities. Where do they
appear in the UI (if anywhere)? Do we display them as strings or
numbers (or icons, or progress bars)? Do the strings identify
single values or ranges of values?

Now I can see why Alexis Kennedy said,
“the fundamental characteristic of ‘qualities’ in the Failbetter
sense is that they are all created equal […] This was a
deliberate design decision […] but now I think it erases too
many potentially useful distinctions. And, in fact, the subsequent
development […] saw many, many attempts to add, or hack in,
different ways to describe the value of, and changes to,
qualities.”

But I still think that problem goes away if you have a set of
building blocks for quality-based narratives instead of a machine
which plays a particular kind of quality-based narrative and
requires new buttons and levers and knobs for every variation that
it wants to allow. I don’t think the StoryNexus restriction of
“all qualities are non-negative integers” is the real problem.
It’s more a question of whether users can extend the system and
rearrange it to suit their needs.

A naive approach to the building blocks style does have its own
difficulties. If you just hand new authors a giant set of blocks
and say, “here, go build something”, that’s overwhelming. How
many different pieces do you have to understand? How many
different possibilities do you have to discard to find a design
you like? But if it’s introduced well it can be very beginner
friendly. You need a good default structure which just works
straight out of the box, and which is relatively easy to customize.
And you need a good cookbook explaining how to do specific common
tasks.

Saturday, June 8

I went through my StoryNexus summary and made a list of things to
implement. Most of them are interface pieces that can be add-ons
and are also easy to implement individually. So I have lots of
little easy tasks now. But there are a couple of things that need
to go into the core. Filtering/sorting cards by priority, for
instance. And I probably need some support for choices succeeding
or failing based on a skill check and a random die roll. And
support for a cancel button (the “Perhaps not” buttons in
Failbetter games). But I think all of those are straightforward.

So the big piece that’s left is displaying requirements so players
know why a particular card or choice is locked. I have an idea how
that will work, but I’ll probably let that marinate for a few days
while I implement some of the little stuff.

Sunday, June 9

I didn’t get any more done yesterday because farm work went into
the evening. But this morning I wrote a first draft of Basic
Abilities (StoryNexus’s stats which get better or worse as your
attempts succeed or fail) and a <<gotoresult>> macro for use in
<<link>>s and <<button>>s. The difficulty checks are code, not
tags, so it’s separate from the other requirements, but I think it
will be OK. It’s fairly readable. If you define a skill:

<<set $cunning to new BasicAbility('broad', 25)>>

Then you can make a button that checks it:

<<button "Button Title">>\
<<skillcheck $cunning 80>>\
<<gotoresult "Result">>\
<</button>>

It will go to the passage “Result Success” if the skill check
succeeds, or “Result” if it doesn’t. If you create “Rare Result”
or “Rare Result Success” passages, it will go to those 20% of the
time. And of course the difficulty could be a variable instead of
a hard-coded number. And it works with <<link>> as well as
<<button>>. And if you leave out the skill check, it will always
go to “Result” (or occasionally “Rare Result”).

This took me much longer than I would have liked (I spent about
two hours fiddling with it), but I’m still figuring out how to
design interfaces that work with the grain of Twine and SugarCube.
I think I’m getting a handle on it, so hopefully the other “simple” pieces will be
quicker. Other types of stat/trait objects should certainly be
very easy. I have JavaScript code somewhere for Choice of Games’s
Fairmath, Inkle’s ratio-of-choices, and Chris Crawford’s Bounded
Numbers, so I might throw those in there just for fun. We’ll see
how my time goes.

I forgot how much other stuff I needed to do this week. The farm is in full swing, and on Monday I spent my spare time working on another project.

My a cappella group rehearses on Tuesday evenings, but I did do some work on how single-use choices get removed from the deck (I was a little confused because cards get removed once the user sees their contents, but choices get removed only when you click a link or button to go to the result of the choice). I refreshed my memory of how Ink and ChoiceScript do things. It’s interesting that Ink presents single-use choices first, while ChoiceScript defaults to reusable choices and you have to say *hide_reuse or *disable_reuse. Though you can also use one of those commands at the top level to change the default. Smart design.

On Wednesday I refined the choice code a bit by adding some code to track which section you’re in so you don’t have to repeat it by hand. SugarCube only gives you the current top-level passage: not any included passages or anything. I wanted the current card or choice as well.

Also on Wednesday I finally got fed up with pasting into Twine’s “Story Javascript” for testing. So I spent a while looking over Twee2 and Tweego trying to decide which one would fit my needs better. They both have about the same number of disadvantages, so I flipped a coin and went with Tweego.

And on Thursday I wrote a first draft of card priority. By default it selects higher priority cards first, but it also allows excluding lower priority cards altogether. So you can use priority as a measure of importance (show these cards first), or you can let urgent events lock out others (you can’t stop to haggle with a street vendor for a meat pie when you’re in the middle of a sword-fight).

I think that’s all the coding things on my list except the big one of displaying requirements to the player.

I haven’t pushed any of this to GitHub because I haven’t tested any of it very well. But I’ll probably put in a couple hours tomorrow morning to kill time until the conference opens…

2 Likes