(removed)

You’re probably right. However, my thought wasn’t so much that I wanted to support IE9 specifically, but if I could at least support IE9 then I could probably be safe in assuming it’d run on all of the weird mobile browsers out there.

So far I haven’t had to abandon any features while trying to support it, although it has made things wordier in areas.

I’d say it’s not even achievable in theory. :)

(Okay, it is achievable, but only by putting all the hard work on the author. Having it work automatically? No.)

Yeah. I know it’s not completely avoidable, but I’d like to do my best to avoid the pitfall of forcing the player to restart their game when you update your game.

This probably isn’t as big an issue with Inform games and the like because updating is generally for bug fixes. Probably the same deal with ChoiceScript games. But there are a lot of Twine games that release weekly or monthly story updates, not just bug fixes. If they decide to add a few lines to something earlier in the story, or move a block of code from one section to another, that seems like a really silly reason to force a restart.

Sadako does actually do a bit of migrating save files. It’s not perfect, but it does a decent job transitioning older save files to current versions. As long as you’re not expecting a default value to have been changed by that point in the story, of course.

FYI Inform has no save-upgrade mechanism at all. If you upgrade to a newer version, you have to throw away all of your old saves; they don’t work at all in new versions, even if the fix was just fixing a typo.

Ah. It’s been like a decade since I messed with I6 and I didn’t play much with I7 so I couldn’t remember how they worked. I thought I6 did have save-upgrade ability for some reason.

I’m glad I never got far with my I6 game then. I was planning on releasing it in a serial manner like I was just saying. That would have blown it out of the water. :sweat_smile:

I released a new version.
https://github.com/Tayruh/sadako/releases/tag/0.14.0

The highlight of this release is that you can now pass arguments to anything that jumps to a page or label, so this is not just normal jumps but also dialog popups and reveal links.

It looks something like this:

## start
    >> #greet >> ["Bob", "Sam"]

## greet
    "Hello!" &:args[0] shouted.
	>> reply >> "&:args[1]"
	"Hi, &:args[1]. My name is &:args[0]."
	<< END

	= reply
	"Hey there. I'm &:args"
	<<
    
// outputs
"Hello!" Bob shouted.
"Hey there. I'm Sam"
"Hi, Sam. My name is Bob."

Perhaps you can see from the example, but arguments are relative to the jump they’re in, even though you use sadako.args to reference it. Returning from a jump restores the calling script’s arguments.

For other methods of calling pages and labels, you just follow the page or label name with >> followed by some JavaScript to assign to sadako.args, which you can reference easily with the shortcut &.args (the variable) or &:args (the value).

So for other options it’d be:

// redirect link
[:greet >> ["Bob", "Sam"]:]

// dialog link
[:* #greet >> ["Bob", "Sam"]:]

// reveal link
[:+ #greet >> ["Bob", "Sam"]:]

This also works for >>= includes, including + >>= choice includes. For example:

## start
    + Test 1
    + >>= #example >> "Bob"
    + Test 2

## example 
	+ Just choose the choice, &:args.
		You did it!
    
// outputs
<Test 1>
<Just choose the choice, Bob.>
<Test 2>
1 Like

Hei @tayruh, I’m designing my own game engine HyperSigil, surprisingly similar to yours in some core ideas, for example being able to run the compiler in the browser, or making sure all HTML can be changed. I decided to try if I could copy your demo “Rainy Day”. I had mild success with that, still a WIP, to be honest, it has been of great help to see the shortcomings of my own game engine. In my defense, I designed the engine for longer passages (like twine), the concept of making paragraphs flow completely evaded me. So, although you may end up being the only user of Sadako (hopefully not), I’ll surely be keeping an eye on it. And I’ll try the VN next.

2 Likes

Let me know when you have an engine to play around with. I like checking out new engines. :slight_smile:

Edit:

In my defense, I designed the engine for longer passages (like twine), the concept of making paragraphs flow completely evaded me.

I’m curious what you meant by “longer passages”. The passages in Sadako can be just as long as in Twine. I prefer to write them short because I tend to think that if you have to scroll the page too much, you’re at the risk of boring the reader, and walls of text are overwhelming. But that’s a preference in my writing style, not a limitation of Sadako.

I’m also not sure what you mean about “making paragraphs flow”. :thinking:

I mean that in your model you show each line/paragraph independently, like blocks, you make choices on what to show next as well as on what to clear, so for example you can jump around from on point within the passage to another and continue the flow of blocks there. For you, passages are simply a way to organize such blocks. In my model, you don’t jump around within a passage, there no clear separable blocks by default, you only jump around from one passage to another, each passage may include conditional logic to determine what to show and what not, but it’s a one time full pass, if you want to change what is being displayed you must change the state and reload the whole passage. This is fine for passages that are mostly static content with variable replacement, but if you have some kind of dialogue system (like the Erin talk in your example) a flow works much better.

So where you do (pseudo-code):

Some text
[Question A?] Answer A
[Question B?] Answer B
-
Say no matter what you answer.

I had to do (pseudo-code, not the real syntax)

<if state = 0>
Some text
[Question A?] <set state to 1><reload>
[Question B?] <set state to 2><reload> 
</if>
<if state = 1>Question A? Option A</if>
<if state = 2>Question B? Option B</if>
<if state = 1 or state = 2>Say no matter what you answer.</if>

I’m trying to mitigate the problem by creating a tag the encapsulates also a flow like logic, so that you can do:

<flow>
    <step>
        Some text
        [Question A?] Answer A
        [Question B?] Answer B
    </step>
    <step>
        Say no matter what you answer.
    </step>
</flow>
1 Like

I see what you’re saying now. Yeah, that’s a feature that Twine doesn’t have. That’s where the Ink stuff comes in. A lot of ideas that you’re praising me for I actually borrowed (read: blatantly stole) from Ink. You should check it out, if you haven’t already.

https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md

I mentioned it a bunch of times in this thread, but Sadako is basically everything I like about Twine and Ink mushed together with a couple items borrowed from other languages.

As for your system, it seems a bit weird that you reload the page after the choice. Why is that? To avoid the need for new passages? Or does your engine do just one big passage?

As for your system, it seems a bit weird that you reload the page after the choice. Why is that? To avoid the need for new passages? Or does your engine do just one big passage?

It’s to avoid making new passages. You could, but if you do you’ll still have duplicated content (even more) and no noticeable benefits, it would look like:

:passage0
Some text
[Question A?] <load passage1>
[Question B?] <load passage 2> 
:passage1
Question A? Option A
Say no matter what you answer.
:passage2
Question B? Option B
Say no matter what you answer.

This doesn’t scale well with a dialogue. Also, the engine only saves state by default when you load a new passage, so if there is no reload the player would lose the choices they’ve made there. But well, I’m happy with the introduction of the flow tag, it’s basically an opt-in for the same feature that can in-crusted in any passage that may need it.

As I see it now, you’ve added twine functionality to ink, and I’ve added ink functionality to twine.

1 Like

Minor update: https://github.com/Tayruh/sadako/releases/tag/0.14.2

I only made two changes, but they both affect previous behaviors instead of adding new features.

  • One change is that destinations for links are now evaluated while rendering the link instead of when clicking on the link. (This is better described in the change log.)

  • The second is that I remembered how stupid case sensitivity is, so I’ve made another step forward in removing it. Page and label names are now case insensitive. An easy example of its convenience is that before you would have to write it like this if you wanted both links to reference the “keys” page:

    [:Keys @: keys:] are sitting on the table. They're the [:keys:] to your car.
    

    But now you can write it like this:

    [:Keys:] are sitting on the table. They're the [:keys:] to your car.
    

There’s more info about these changes the change log.

If anyone is actually using this and plans on updating, you should read the change log info because both of these are potentially breaking changes if you’re:

  1. using a link to redirect to different locations based on evaluation (using #=page_name or %=label_name).

  2. referencing sadako.page (or &.page alias) to determine which page you’re currently on, because those names may have changed in this version.

Anyway, that’s it.

How important is the sequencing of when tags are handled? I had an idea to use tags on choices that affect and are affected by variables (e.g., character stats). For example, you might have $.fear representing the player’s fear level, and you could use a tag to mark choices that increase the level. For example,

+ [Open the mysterious box] ~:scary

When the choice is displayed, we can use doChoiceTag to annotate the choice as being scary, or warn that this choice will put the player past some sort of threshold, or whatever.

If the player picks the choice, then doLineTag can increase the fear level.

This mostly works, but Sadako currently calls doLineTag fairly late in the rendering process, so any code in the next section will be executed before the variable update. I think the current assumption is that tags are mostly for visual effects, so what I’m describing is not the intended usage. Obviously, this could be done with macros or in-line code, but being able to mark choices this way would be much more succinct. (Taking this further, we could allow doChoiceTag to control visibilty of choices. Like, if the the character is too scared, then scary choices won’t show up or will be crossed out.)

Another possibility would be to add a doSelectedChoiceTag hook that gets called by doChoice.

Thank you for trying my engine. :slight_smile:

Unfortunately what you’re trying to do is not something I considered but it’s definitely something I’d like to add. I like the idea of processing the tags after a choice click and having the functions for altering display and performing the actions separate, so I think I’ll go with that.

Another method I thought would work would be to use a macro in the actual choice line, so it’d run the macro after you clicked as long as you put it after the [] part in the choice. Unfortunately, that doesn’t seem to work because I apparently have it rendering the macros even before you click the choice. This is a bug. Oops.

Other than the issue you’re having right now, how do you feel about Sadako? I haven’t had anyone mess with it enough to give me any actual suggestions or constructive criticism.

1 Like

It’s been a while, but I finally released a new version of Sadako. You can find it here: https://github.com/Tayruh/sadako/releases/tag/0.15.0

The main feature of this release is the request put forth by @zednenem: I have added sadako.doPageTagAction(), sadako.doLineTagAction() and sadako.doChoiceTagAction() functions that trigger when processing an item that has tags on it. These are for manipulating variables and the flow of the script during the processing of the page, as opposed to sadako.doLineTag() and sadako.doChoiceTag() that only alter the display of a line after the entire page has been processed.

Here’s an example of it in action using something similar to the example given.

// javascript
sadako.var.scary = 0;
sadako.doLineTagAction = function(tag, value) {
	if (tag === "scary" && value !== null) sadako.var.scary += parseInt(value);
}

sadako.doChoiceTagAction = function(tag, value) {
	if (tag === "scary") {
		sadako.var.scary += parseInt(value);
		if (sadako.var.scary > 20) {
			sadako.doJump("#you_dead");
			sadako.end();
			return;
		}
	}
}


// sadako script
## start
	This is creepy. You're feeling about $:scary% scared right now. ~:scary:5

	+ This isn't that scary. ~:scary:2
		Phew. You managed to get away with only $:scary scared points.
	+ This is pretty scary! ~:scary:100
		You won't see this line.
	- The end.

## you_dead
	Oh noes. You ded.

This outputs:

This is creepy. You're feeling about 5% scared right now.
<This isn't that scary.>
<This is pretty scary!>

If you click choice 1:

This isn't that scary.
Phew. You managed to get away with only 7 scared points.
The end.

If you had chosen choice 2:

This is pretty scary!
Oh noes. You ded.

Anyway, that’s about it. My apologies for taking so long. Implementing this required reworking a bunch of important functions like how choices and jumps worked, so it was a bit like playing whack-a-mole with bugs for a while there. :sweat_smile:

Actually, this reminds me of Flutter. It has a hot-reload feature. If I understand it correctly, flutter compiles your code into a “virtual DOM” of sorts and when you change a line of code it only reloads that specific node and its children if necessary. So I imagine it would be possible, albeit extremely troublesome to implement. If you parse the IF source file into a tree – it has to be deterministic –, then you could relate save states to position in the tree and not dependent on the label name or line number.

About new variables, you could hoist and instantiate the variables before loading the save, variables that didn’t exist before would receive a default value, and variables that stopped existing would be discarded. The only problem is for variables that change names. But you can’t avoid all pitfalls.

I’d imagine Flutter works similar to how a diff file works (like github, for example). But doing so requires having the previous source available, which hot-reload would have. But loading an old save file for a newly downloaded file wouldn’t have information available, so I wouldn’t be able to compare changes in the tree structure. Just like how Flutter compiles everything from scratch on boot and only does the hot-reload while it’s running. (From what I remember, anyway. I haven’t played with it much.)

As for instantiating the new variables, that’s what I do. You can check out the function that does it here. It’s a simple function but it doesn’t need to be very complex for its purpose. sadako.default_data is just a copy of the current state when you call sadako.startGame(), as seen here . sadako.updateData() just fills in all of the missing variables in the save data so there is no weirdness.

1 Like

Depending on how you implement it, you could keep a record of the sequence of nodes visited, like Ink. Then check if the same path can be traversed. The problem is identifying nodes in a deterministic way. Or maybe it’s not a problem, I honestly don’t know.

My idea of using the labels as “save points” came about from the fact that Ink fails spectacularly at solving this problem. Literally placing a single extra line between the start of the game and your current save point is enough to break your entire save file.

Ink is first and foremost a scripting language meant to be placed inside another system. Like dialogue trees in a game like Mass Effect, for example. You’d use labels (knots, I think they’re called?) as jump on points and never save in the middle of a dialogue tree. That way you can alter the ink file without breaking anything.

Ink used as a straight choice game in HTML is just an afterthought and has a lot of pitfalls. The default HTML file that Inky spits out doesn’t even come with a save system; you have to roll your own.

I understand it. I don’t think Ink tries to find your location by position on a tree. It generates implicit labels (not exactly) for many things. That’s why changing a line breaks it. Because it regenerates the implicit labels. And that’s why it has to be deterministic (but that’s the real challenge).

I used it as an example in the sense that Ink keeps a log of visited labels.

You’re right, each system caters to different needs.

I do like Ink’s syntax. I find too much markup distracting. One of the reasons Twine’s story modes didn’t grow on me. :frowning:

1 Like