Sadako: a new hypertext adventure engine

I’ve spent the last few months working on a project that I’d like to share with the public. My goal was to create a scripting language that merged my favorite things about Twine and my favorite things about Ink into one package, and I feel like I’ve succeeded.

Sadako is written entirely in ECMAScript 5 compliant JavaScript and is meant to be included in an HTML page. However, I left the text output functions exposed so that they can be easily overridden with functions targeting a different UI, if anyone wants to include it in an Electron app or something similar.

I have two demos created in this engine:

“Rainy Day” is a very short demo, but it comes with commented source code. You can find it here: https://tayruh.github.io/rainy_day/

“Monster” is a pet project of mine, but I have the first “chapter” available. It’s reportedly 30 minutes or more in length. The source is much more complex, but it isn’t commented. You can find the game here: https://tayruh.github.io/monster/

Some other links:

Here’s a snippet of what the script syntax looks like:

## remote
	It's the remote for the television. Erin is looking for this.
	
	+ [Use]
		~~ if ($.bookmark === "erin.remote")
			[:&
				sadako.closeDialog(true);
				game.move(null, "remote");
			:]
			You hand the remote to Erin.
			"Thanks, $:name!"
			She points it at the TV and turns the volume up a couple notches. "Ah. Much better."
			+++ {gave} [Back]
				>> #living_room
		~~ else
			You can't find a use for the remote here.
			
			[:& sadako.doLink("#inventory") @: Back:] ~:choice
			<< END
	+ [Back];; >> #inventory

One of the big features of Sadako is that you can either compile the source to a JS file to be included into your page, or you can write the script directly into an HTML page. The following is a fully functioning (albeit incredibly short) game.

<html>
    <head>
        <style type="text/css">
            .choice { 
                cursor: pointer;
                color: orange;
            }
        </style>
    </head>
    <body>
        <div id="output"></div>
        <textarea id="source" style="display:none">
            ## start
                Hello world
                + Choice one[] was chosen.
                    Maybe try choice two next time?
                + Choice two[] was chosen.
                    Maybe try choice one next time?
                - The End
        </textarea>
    </body>
    <script src="sadako.js" type="text/javascript"></script>
    <script src="kayako.js" type="text/javascript"></script>
	<script type="text/javascript">
        sadako.init();
        sadako.startGame();
    </script>
</html>

Anyway, I guess that’s it. Hopefully you guys will check it out and let me know what you think.

Edit: Rewrote the description and changed the title because I don’t think that I presented the information very well the first time around.

7 Likes

I was unsure whether to keep updating this thread as I add features, but I figured that it’d make sense if I added big ones that might be of interest. shrug

After realizing how useful scenes are for Inform and TADS, I decided to add them to Sadako. And so I did. As far as I’m aware, I managed to emulate their functionality almost exactly.

To be honest, it comes at a good time because I was trying to figure out how to prevent the game I’m working on from turning into spaghetti code. This will solve a lot of those problems. :smile:

Anyway, I updated the demo code in the first post (and also the demo itself, but the changes aren’t visible), and added the info on how to use it to the reference. There’s a direct link to the section right here.

2 Likes

In case there’s any passing interest in this project, I finally managed to update the JavaScript reference for it.

Part of this delay was procrastination and part of it was because I was waiting for the project to become stable enough that I was confident none of what I wrote in the reference would change.

With exception of the init and startGame functions, it should possible to make a game in Sadako without using any JavaScript at all. However, you can enhance your game through JavaScript quite easily using a few functions and global variables that are provided for this purpose. And this is what the reference covers.

You can find the reference here: https://github.com/Tayruh/sadako/blob/master/javascript_reference.md

4 Likes

I just released a complex demo which I think would be very useful for anyone curious in this system. It’s basically chapter 1 of a game that I’m currently working on.

The game itself is written as hybrid fiction that attempts to emulate a parser-style game world by using hyperlinks as navigation and object interaction. Sadako can be used to write games in a typical Twine style or even an Ink style. This game blends the two styles together.

You can find the game here: https://tayruh.github.io/monster/

And the source is here: https://github.com/Tayruh/tayruh.github.io/tree/master/monster

An example image:

2 Likes

I can see how the scripting is like Ink-- what features of Twine did you replicate? The passage-based model?

1 Like

Yeah. Here’s a quick list of similarities:

  • The passage model (I call them pages, but same thing). I added labels inside pages though, so it’s like mini-passages inside a passage. I think it helps prevent using passages for only a few lines of text, which I personally found annoying. Pages also support tags like in SugarCube.
  • The embedding of variables values into the text. For example, if you have the character’s name stored as name, it’ll display with $:name in your text, similar to $name in SugarCube.
  • Simple span tag markup. @@bleh;Some text.@@ in SugarCube is <:bleh::Some text.:> in Sadako.
  • It has variables automatically saved to storage using the $ prefix and deleted after transition if prefixed with _, similar to SugarCube.
  • Simple text replacement based on true/false conditions (also present in Ink) with You have {:some_value > 0::some stuff::nothing:} left.
  • The ability to use javascript inside a page without it being defined ahead of time.
  • You can define a function as a macro and call it from your page with (:someFunction arg1, arg2:) without having to drop into JS to call it.
  • You can embed input textboxes and text areas into your script. [:> $.name @: What is your name?:] is a single line textbox and [:>> would be a large textarea box.
  • It also has history with a definable limit (including disabling). The back() function sends you back one history state.
  • Sadako also provides the functions for showing and hiding a dialog window. Unlike SugarCube, the HTML behind this is up to the user. You can see an example of how to do it in the demo I linked in the first post. Due to the way it’s designed, the “dialog” could also be another DIV (like a sidebar or something) instead of a pop-up.
  • There are before and after user defined functions that are called before and after every page transition. You can define them so that they are only called for a specific page, or you can use ALL to have it called for every page.

That’s about it, I think. Twine/SugarCube has a lot of high level features that I honestly have no intention of ever adding. But I love their passage model and their embedding stuff, so I implimented it.

Before I started Sadako, I originally tried just using Ink inside SugarCube, which I did actually get to work. But the two different engines had a really difficult time talking to each other. It was possible but incredibly cumbersome. And that’s why I developed this. It was mostly an engine I made for me, but then I decided to share.

6 Likes

I just wanted to mention that I spent the last day or so updating the script reference docs. I completely proofread them and fixed any grammatical errors and also corrected/removed any erroneous information left over by changes in the code (oops!).

But even better is I added a table of contents. The reference is nearly 1500 lines, so it was pretty easy to get lost in (I wish github offered a sidebar for markdown).

Sadako is pretty much exactly where I want it, so any changes are just small features added, tweaks, or bug fixes (which seem to be completely squashed at this point, thankfully). Nothing that severely logic changing like in the past, so the docs shouldn’t change much more, I would think.

The reference is here: Sadako Script Reference

3 Likes

It’s late and I’ll modify the reference to reflect the changes tomorrow, but as @HanonO suggested, I added reveal links and simple dialog popups to the engine.

You can see a working demo here: http://tayruh.thanatos.feralhosting.com/gamebook/

And the source to the demo is here: http://tayruh.thanatos.feralhosting.com/gamebook/test.sko

Giant Edit (killing wall of text update):

I spent all day modifying and optimizing the stuff I posted last night. It’s pretty slick now, if I do say so myself. The opening and closing of the dialog window via dialog links works a lot more similar to how AXMA does now.

I updated the example and source in the links above with more things to do, if anyone wants to check it out.

I also updated the reference. The direct link to the reveal text stuff is here with the dialog link stuff directly below it.

Edit:
For anyone curious, I polished up the code some more and added some more features. You can close the dialog with a simple [:*!:] command now, which is pretty handy. (It’s called immediately without a link name, or added as a link if a name is provided.)

I also updated the getting started tutorial with an example that has a ready made dialog box so you can copy & paste and get a game up and running right away.

Triple edit?:
You can now set the title of the dialog, which is something I wanted to do in the beginning, but I wasn’t sure how to make the command simple to write at first. It ended up being a “duh” moment in the end. It goes like this [:* Dialog text. @: Link name @: Title name:] The title doesn’t have to be included or can even be given a blank assignment.

2 Likes

I have an exciting update (for me, anyway). :smile:

For the less exciting part of the update: I added cycling text links.

It’s written like this: [:+> $.color @: blue::red::purple::yellow:]

It cycles through that list of items and stores the currently selected one into the variable ($.color in this case). If you come back to this section of the story again, it’ll look at the given variable and will automatically display that value as the selected item.

Like usual, you can use @:= to evaluate content on the right. In this case, you can pass it an array or a variable that holds an array and it’ll use that instead of parsing the text like above.

The demo is in the same location as before, but it’s been updated. You can check out here.


The final boring part is that I completely rewrote the functions involved in processing the tags and writing the lines and choices to the screen. It’s actually a really significant change, but it’s one that you can’t see at all, which is good. I think. :stuck_out_tongue: But that change leads me into the exciting part…


I made a visual novel demo with Sadako! :grin:

You can the check the demo out here: https://tayruh.github.io/visual_novel

(Yes, the art is totally stolen from Higurashi. Shhh )

One of the main goals I had writing this engine (as I mentioned in the topic post) is that I left the writing output and such exposed. I put a lot of effort into guaranteeing that functions containing any HTML at all is able to be overwridden by the end user so that they can just modify a few functions to their liking without touching the core.

Using this, all I had to do was overwride three functions to get this working. The needed javascript source is only about 145 lines. The sadako script is like 14 lines.

You can check out the sadako script here, and the javascript is here.

The rest of the source is here, if you’re curious.

3 Likes

Very impressive!

3 Likes

I agree with @HanonO, it could really give interesting visual stories.

Just one thing : maybe you should put an explicit icon (on the mouse pointer) to show that one can click on the bottom panel to make appear/disappear the characters ?

And did you write a manual to handle this ?

(Uh, these testers, give them a stuff, and they want more ! :innocent:)

You mean the game characters or text characters? I’m kind of confused on what you’re saying. To progress the text, you should be able to click anywhere on the screen. If you’re clicking somewhere and it’s toggling the character images somehow, that’s a bug that I haven’t seen. :thinking:

But yeah, there isn’t any tutorial or guide or anything. It’s just something I whipped up in a few hours to see if I could get it to work. :sweat_smile:

I could probably try to flesh it out some more if there’s interest in it. I don’t think I could ever get it to compete with something like Renpy though.

I also apologize for the demo being fixed at 1280x720. I tried to make it scale with the window, but I was having issues getting the text box and characters to not move all around. I figured it wasn’t worth the effort for a proof of concept demo.

You’re right, I didn’t see that the click worked everywhere ! Maybe I’m not used to this kind of visual interface.

The scale for the window works well for me (I got a small screen).

You should ask to Renpy users if they want another engine ! Maybe they could be interested ?

1 Like

For anyone interested in checking it out, I added lip synced animation and scrolling typed text to the visual novel demo. It looks pretty sweet, IMO. :slightly_smiling_face:

https://tayruh.github.io/visual_novel

Another update to Sadako. (Hopefully I’m not posting these too often.)

This one is actually a pretty big one since it affects the core functionality of the engine.

You can now write token blocks inside of token blocks. Tags inside of tags, in other words. Originally Sadako couldn’t do that. It was easy enough to get around, but now you don’t have to.

Here are some examples. For those not familiar with the syntax, I have a normal block for the first example of each and then I show one inside of another in the second examples.

Inline conditions:

{:1 == 1::this is true::this is false:}
// outputs: this is true

{:1==1::{:1==0::asdf::bleh:}::blargh:}
// outputs: bleh

Spans:

<:bleh::some text:>
// outputs: <span class="bleh">some text</span>

<:bleh::<:asdf::test:>:>
// outputs: <span class="bleh"><span class="asdf">test</span></span>

Script blocks:

[:= "bleh":]
// outputs: bleh

[:= "[:= 'test':]":]
// outputs: test

Macros:

// javascript:
sadako.macros.test = function() { return "Hi!"; };
sadako.macros.test2 = function() { return 'Test macro says, "(:= test:)"'; };

(:= test:)
// outputs: Hi!

(:= test2:)
// outputs: Test macro says, "Hi!"

You get the idea. You can also have varying kinds of blocks inside each other. It was just easier to show the example with the same kinds.

3 Likes

This is embarrassing, but if anyone grabbed the copy I uploaded last night, you should probably grab the version that I just uploaded. :sweat_smile:

It was a good first effort, but my attempt at putting tags inside of tags broke pretty spectacularly if you tried to put inline conditions inside of inline conditions. The issue is that they parse the different options with the :: token, so the parser was choking when it’d see the :: inside the tag in one of the options… if that makes sense. I worked on this all day and nearly broke my brain, but I think I have it now. :crossed_fingers:

I can at least guarantee this works, which would have broke before:

[:& 
    _.name_check = "[:= '<:character::' + $.name + ':>':]";
    $.age = 30;
    $.gender = "male";
    $.name = "Bob";
:]

My name is _:name_check. I'm a{:$.age > 25::n old:: young:} {:$.gender=="female"::{:$.age < 18::girl::woman:}::{:$.age < 18::boy::man:}:}.

// outputs: 
My name is <span class="character">Bob</span>. I'm an old man.

An important change to note is that [: :] script blocks no longer render tokens inside them. They replace variable names (so $.name becomes sadako.var.name), but the string it’s assigned remains [:= '<:character::' + sadako.var.name + ':>':], which is why we can assign the name to the variable after that string had been set. It won’t look up the variable value until it’s printed on the line.

Anyway… that’s it.

This is all great. can’t wait to see how it evolves.

BTW nice taste in visual novels.

1 Like

Thanks!

And yes, Higurashi is awesome. :smiley: I’ve been going through it lately. I’m currently on volume 4. I’ve been watching each arc of the anime to compare it after finishing a volume too. Hehe. I’m a dork.

After this, I’ll probably do the same with Umineko.

1 Like

Those two are my all time favorite visual novels. Especially Umineko, That is a masterpiece.

1 Like

I got bored and made another demo.

This time I took my 15 year old text adventure engine and made it work using sadako script. The engine is almost entirely in JS, but it takes the definitions of the objects from sadako script. Because of that, you can have use all of the normal sadako script stuff, including jumps and returns, all the fancy links and dialog windows, etc. However, due to the hacky nature of splicing the two together, choices aren’t working in this demo. Sorry about that.

The demo is here:
http://tayruh.thanatos.feralhosting.com/text_adventure/

You can see the sadako script here:
http://tayruh.thanatos.feralhosting.com/text_adventure/textadv.sko

As far as the usability of the engine, it’s not that bad. I actually got quite a lot of functionality to work way back then.

1 Like