(removed)

I didn’t mean to discourage you! There’s hope for Sadako yet.

Most people choose an IF platform by playing a great game and saying, “I really like this game, and I would like to make another game just like it. How did the author(s) make it?”

So, when IF platforms successfully take off, they require an admirable story (not just a technology demo) to attract new authors. Historically, the first “admirable” story for each now-successful IF platform was typically either written by the platform authors themselves, or directly funded by them. (Twine’s first admirable story by Anna Anthropy is the only exception I’m aware of.) Admirers don’t seem to directly care about any of the details of the system, except that if it’s too hard for them to learn the system and finish a game, that’s a major factor in achieving true popularity.

If you’ve developed a platform just for you, that’s awesome, and worth your time. If you make games you want to make, especially if you keep up with it, you’ll build a following, I assure you.

Another thing that intrigues me about Sadako is that you can use it to create visual novels. That space is ripe for disruption, IMO. The only major free tool for developing visual novels is Ren’Py.

Ren’Py is good, but it has a huge flaw IMO: it doesn’t support building games that you can play on the web. This is a deep architectural issue, due to Ren’Py being implemented in Python. (I mean, I guess you could try to ship it with https://pypyjs.org/ but even a “hello world” pypy.js program requires 4MB of WASM. You wanna run that on a phone?!)

Furthermore, Ren’Py has very limited support for building native apps for the iOS App Store. (The Ren’Py home page says that it supports iOS, but if you scratch the surface a bit, you’ll find that Ren’Py’s native iOS app support is a “work in progress.” (Ren’Py’s author Tom Rothamel, was extremely opposed to the iOS App Store when it first launched.)

If you make a free visual-novel tool that supports web and iOS, and especially if you use it to ship a game, you’ll definitely get some interest.

3 Likes

Yeah, sorry. I was already discouraged and took it to heart more than I should have. The “why am I even doing this?” question has been an ongoing internal struggle for me.

Also, Renpy has been working on a web version. They even have it included in their compiler now. https://renpy.beuc.net/

But you’re not wrong that it’s mostly an untapped market. It’s basically only Renpy or really questionable extentions for Unity if you want to make a VN. (There are other engines too, but unsurpringly they’re all documented in Japanese and they don’t support web or anything other than windows.)

I’ll think over changing my syntax a bit more. After really thinking about it, I have all of the parsing of the story script done in Kayako and then it outputs it to a data object/JSON file that it passes to Sadako. In theory I could make the syntax whatever I wanted and as long as it compiled to the same JSON, it would require little to no change in the actual engine.

Goodness, I didn’t mean for my syntax nigglet to blow up like that. JavaScript interop is great, but just make it able to call an arbitrary JS function. Half the point of a if tool is getting it done with less syntax and not writing one’s own library.

The interface to Monsters really is good.

1 Like

Nah. It’s not just you. If you skim this thread you’ll see that mostly the only comments I get are “why the heck did you choose this syntax?” It’s just an ongoing thing.

As for the JS, I like being able to do this, but it’s not really necessary, I guess.

Welcome to $:locations[$.player_location].name.toUpperCase()! I hope you enjoy your stay!

I just clicked around on RenPyWeb, and, I can’t believe it, they actually shipped pypyjs.

This is completely unplayable on a phone. It’s barely playable on desktop Chrome.

https://dullachan.itch.io/the-show-must-go-on

Please allow the game a minute or two to load.

A minute or two?!?! A JS implementation would have a game up and running in less than a second, particularly if it was designed not to download all art + sound at launch, but to load the initial assets right away and then download the rest of the assets in the background with requestIdleCallback.

You could do better than this, no problem.

1 Like

Haha! :smile: I never really looked at it. I just assumed they actually ported it. That’s pretty horrible.

They wrote up a page on possible performance enhancements that make it extremely clear to me that they have picked entirely the wrong approach to web performance.

RenPyWeb is currently reasonably fast (for a Ren’Py/Python/C/WebAssembly stack :)).

:))))))

2 Likes

this is actually how i tried to get the inklecate compiler working in the browser for a bit. in that case, the perf problems don’t matter as much, because it’s for a REPL, and it’d do something nobody else had. but even then, it required loading all of mono,js (which in practice is all of the code for every mono->js interface, even sockets), then all the DLLs for the entire CLR through mono.js, then the ink engine and compiler DLLs, then the module was ready for use. it broke the first time the external library component was upgraded and i gave up. an absolute nightmare!

1 Like

:scream:
:no_entry:
:no_good_man:
:no_entry_sign:
:skull_and_crossbones:

… it sounds kinda cool tho

3 Likes

yeah i think it was about 10MB of DLLs minimum and the response i saw constantly from the emscripten side of things was “DLLs are highly cacheable.” ok :+1:

needless to say i think it’s gonna be a bit before WASM is a serious consideration for web-based hypertext games.

1 Like

I wrote up a quick concept of what the new syntax might be. It ended up being something visually similar to YAML, but doesn’t follow its same logic.

  • Nesting will be done based on indentation, similar to Python and YAML.
  • A - will precede a command and a + will precede text.
  • Lines not beginning with either token will be considered as values associated with the previous command/text line (indentation will be ignored), like tags and such. You can also use ;; to achieve the same effect.
  • Like current sadako script and Ink, it will collect choices and then wait for input when it sees something that isn’t a choice. Using a - or + without text is enough to separate a group of choices from a group of choices.
  • The do command is a script block, so it’ll be allowed to have multiple lines. That’ll be the only command that allows that.

I think that’s it. The rest should be pretty self explanatory. Any thoughts on this?

Concept Example
- page: erin;; tag: bleh

  - label: intro
  + Erin is squirms in her seat, nervously tapping her foot. Her eyes are still fixed on the office door, shaking her head in disbelief.
    class: dialogue
  - jump: talk_menu
  
  - if: %.erin.intro_talk
    - do: 
      _.x = 1;
      _.y = 2;
    + "Come on. We should start looking for {:%.erin.intro_vanessa::Vanessa::the tenants:}."
  - jump: go.eoc
      
  - label: intro_talk
    + Erin looks at you with a frantic expression. "$:name! What are we going to do?"
    
  - choice: "Maybe Vanessa will know what to do?" 
      label: intro_vanessa
    + Erin groans. "Dad didn't even tell us who she was." She scratches her scalp in aggravation. After a sigh she says, "But, yeah.. We should probably try to find her."
  - choice: "Manage the estate, I guess."
	+ "Well, yeah. But isn't this a bit much? I know you've had management experience, but this place is ginormous and we don't know anything about it. We haven't even met the tenants yet!"
  - 
  
  - choice: "Then let's go do that."
  + "Right. Let's do this."
  - jump: go.eoc

  - label: following
  - if: $.room === "office"
    - if: %.erin.following_office_father
      + Erin flicks some papers on the desk while mumbling to herself. She doesn't seem to be in the mood to talk.
    - jump: go.back
	
  + Erin is idly playing with things on your father's desk.
  - jump: talk_menu
  - label: following_office_father
  + Erin just shakes her head. "Dad is so uptight. Everything is so neat and tidy." Her eyes narrow. "I just want to mess it all up."

  - choice: "Do you dislike our father?"[] you ask.
    + "Dislike?" She tilts her head in thought. "No, not dislike. Just.. He left us, ya know? I'm not sure I can forgive him for that." She scrunches her face into a scowl and says, "And <i>this</i> situation isn't helping."
    - jump: go.eoc
	    
  - while: 1 == 1
    + This loops forever.
    
  - do: _.a = 0
  - for: _.a == 0; _.a < 5; _.a++
    + The value is _:a.
    - if: _.a === 3
      + Breaking early.
      - break:
    
  + The end.
3 Likes

Bouncing off what Dan said about needing an admirable game; perhaps consider participating in the fast approaching ECTOComp’s 4 hour “Petite Mort” category. Nobody expects a masterpiece in only 4 hours so it seems like a low risk and low (time) cost opportunity to get an example game out. You could also do a screen recording as you make your game which might drum up more interest. :slight_smile:

2 Likes

Dramatic improvement. Ship it. (Not in the fanfic sense.)

2 Likes

Well, against all advice and common sense, I’ve decided not to change Sadako’s syntax. This will probably be the death of Sadako (which sounds like one The Ring’s sequels), but that’s just how it is.

I spent literally all day yesterday from wake to sleep plotting out a new syntax and then implementing it, and I probably got about 30% complete when I realized that it just isn’t going to work for me. In order to use another syntax, I need to go against my original design goals, which really rubs me the wrong way.

At the risk of sounding selfish, I designed this for me and no one else. Not in the beginning at least. As time went on I decided to share with others, but not if it means sacrificing what I valued in it.

Basically my design goals were this:

  • Support all browsers back to at minimum IE9 (ES5). I’ve had sketchy results with minimizers supporting older browsers, so I just keep everything old school with for loops and wrote my own string replacement function instead of using template literals.

  • I wanted you to be able to write your script directly into your HTML file. A compiler is nice because you can split your files up, but for simple tests and games, you can write your story script right into a textarea tag in your HTML file and it’ll turn it into a game. That’s why Sadako is three files: sadako.js (the game engine), kayako.js (the compiler), and kayako_cli.js (the wrapper to turn kayako.js into a command line compiler).

  • I wanted to you to be able to rewrite all of the output sections of the code to suit your needs. There isn’t any HTML stuff buried in the engine. It’s all separated into exposed functions that can be changed. This is how I made the text adventure and VN demos.

  • You need to be able to save in the game whenever you want. Changing the script should not break your save file. This unfortunately comes at the cost of having to assign labels to choices or having a bit of progess lost during choice blocks, but this is my workaround for an issue inherent in the Ink syntax that I based my original concept on.

  • I wanted my script to be as concise as possible. I like to keep my line counts to a minimum.

    • A lot of my ideas for that were taken from Ink, which is where the token counts comes from. Token counts are a bit ugly, but they avoid bracket matching or space-sensitive indenting.
    • Bracket matching uses too much vertical space for me, but it also falls apart on choice blocks for me, because they can be so long.
    • Space-sensitive breaks the line separators (the ;; token), which means every command would have to be on a new line, which also kills vertical height. In other words, this:
      + Choice;; You picked a choice!;; >> next_page
      
      would become:
      + Choice
          You picked a choice!
          >> next_page
      
  • I wanted the story text to be your focus, not the script. The entire reason I chose to use a bazillion tokens was because languages like SugarCube and ChoiceScript become difficult to read for me when you’re forced to parse out the reserved words when reading your story. The code and the dialogue all blend together and it becomes messy. This is not an issue in normal programming where the reserved words intentionally stand out because the rest of the code is all tokens and numbers. Writing interactive fiction requires a different kind of syntax priority, IMO.

  • Related to conciseness, I wanted to do a lot with as little as possible. Like this line:

    {test} Click [:*#= "page" + (1 + 2) @: here:] to see more. :: !%.test
    

    This assigns the label “test” to this line so you can jump to it later, as well as count how many times the line has been seen. The part at the end of the line only displays the line if hasn’t been seen yet (ie. it’s seen count is zero). The script block adds a link named “here” that displays a popup box containing the content of “page3”. How would I write that in another syntax?

    <<set $test +=1>>
    <<if $test === 1>>Click <<link "here">><<run Dialog.wiki(Story.get("page" + (1 + 2)).text); Dialog.open()>><</link>> to see more.<</if>>
    

    I can’t even make out the sentence from the code in that.

  • I want you to be able to write javascript directly into your story text, not walled off into a script block (which you can also do). For example this works:

    Welcome to $:locations[$.player_location].name.toUpperCase()! I hope you enjoy your stay!`. 
    

    The side-effect of this is being forced to use odd tokens that wouldn’t clash with JS. That’s why tokens like ::, ;;, [:, {:, etc exist. They would be syntax errors in JS.

I guess that may be it? Anyway, I didn’t choose the syntax haphazardly. It kind of formed to fit my needs and meet my criteria. I understand that it’s not the prettiest thing to look at.

And to be clear, I’m definitely not trying to suggest my language is better than others. It’s just designed to correct annoyances that I had with other languages. So it’s better for me, but that’s not to say it’s better in general.

Sorry for the huge post. I don’t think I ever really shared where I was coming from on this, so I thought it’d be good to share my reasoning.

4 Likes

This is a really crucial point. ChoiceScript started with the theory that there would be a sigil at the start of a line to indicate “this is a line of code.” In ChoiceScript, all code lines begin with a command, each of which starts with a *, so if a line starts with *, it’s code, and otherwise, it’s text.

Only later did I decide to add ${} inline variable replacements, which allow some code to intermingle with text on the same line, and then, reluctantly, I added @{} multireplace, which absolutely does have the problem you describe of intermingling code and text.

What convinced me to go with ${} was that the syntax was pretty distinctive, and that it’s straightforward to write a syntax highlighter for it, and typically only a single token (a variable name) would appear in the braces, so it would be fairly legible regardless. (I think @{} might have been a bad idea, but it was highly demanded.)

I wonder whether the moral of this story, especially apropos the accessibility thread from the other day, is that hypertext source (text containing inline links) is inherently hard to read.

I think hypertext is particularly hard to read when the “link” is really acting as a button that runs a few short lines of code. In React JSX, you can write:

You can click <button class="link" onclick={(e)=>e.preventDefault(); const foo = getFoo(); setBar(foo + 1);}>here</button>
to make the thing happen.

But this totally blows up the flow of the sentence. Generally speaking, people recommend that you define a quicky function elsewhere:

const clickHandler = (e) => {
  e.preventDefault();
  const foo = getFoo();
  setBar(foo + 1);
}

return <div>You can click <button class="link" onclick={clickHandler}>here</button>
  to make the thing happen.</div>

That’s somewhat better, but it’s still pretty hard to read the sentence. Your approach of cramming the code into a few short (but not-yet-taken) bits of punctuation represents a totally different approach. It seems to me that it would be much harder to learn, but it might be easier to use if you can mentally recall what the punctuation does.

But if you don’t use inline hypertext, if you just don’t embed buttons in the middle of your story text, the problems mostly just go away, including some of the more serious accessibility issues.

2 Likes

I think this goal is not achievable in practice.

Just because you’ve labeled your blocks doesn’t mean it’s safe to resume from an arbitrary point in the story. Consider this example:

  1. You release v1 with three parts: chapters 1, 2, and 3
  2. The player plays through v1 chapter 2
  3. In v2, you introduce a new variable who_is_the_killer, that you set in chapter 1 and use in chapter 3
  4. The player resumes playing from chapter 2

What’s supposed to happen now? The who_is_the_killer variable never got set in chapter 1, so it can’t be read/used in chapter 3.

If you think of the game state (all of its variables and their values) as a database, any change to that database’s schema will require a migration. There’s no general-purpose way of automating database migration.

And, for that matter, what if the script deletes a label?

In ChoiceScript, we handle this by restarting the current chapter when you upgrade, which usually doesn’t lose very much play time, and by migrating old saves that have missing variables by adding pre-defined default values. That doesn’t guarantee that upgrading will be bug free, not by a long shot. (We mostly just ignore the bugs that result.)

Yeah, I get what you’re saying. But basically I meant that if you had the code like this in Ink:

+ Choice A
	Answer 1
+ Choice B
	Answer 2

and then you choose choice A and save, then edit your code to be:

+ New Choice
	Oops. Forgot one.
+ Choice A
	Answer 1
+ Choice B
	Answer 2

If you load your save, it will crash. This is because Ink creates the ID of the choices relative to its position in the list. Adding a new entry bumps them all down and breaks the save. If you added it after Choice B, it’d be fine.

My solution (though not optimal, but at least avoids that issue) was that it simply doesn’t save if you’re in a choice block. It creates a save state that will be saved whoever you click a link that redirects to a page or a label or if you choose a choice that has an assigned label. When you save, it’ll write that save state to storage that way on loading your game it will jump you to watcher page or label was in that save state. This means you can move it wherever you want in the code and it won’t break.

So to create save safe code for choice you just do:

+ {a} Choice A
	Answer 1 
+ {b} Choice B
	Answer 2

Labels are only unique to each page, so it’s not a big deal if you use similar names on each page.

Or you can be lazy like me and only slap a label onto ones where it’d be worth saving, like if it exited a conversation or got you an item.

Yeah, I guess I’m saying that this isn’t really a full solution, and it’s not clear that the added friction of labeling a bunch of options is worth the price. Just rewind to the start of the file or whatever.

(Other commercial systems store the game state in a DB instead of in a text file; the GUI editor autogenerates IDs for each choice point.)

Yeah. I’m not sure what a great solution is though. I really dislike the idea of having to worry about breaking saves if you add a single line of code, as is the case with Ink. Twine avoids the issue by only using passages.

Edit: For some reason it took longer than it should have for me to get what you were saying.

You’re probably right that it’d be easier to just rewind to the last page or something. I don’t do chapters like ChoiceScript, but the pages would work just as well. But I like the feature of being able to hit refresh on the page after compiling and it’s right where it was. Also, I got the label on choices to work, so it’s bit too late now. :stuck_out_tongue:

Oh, to be clear, the labels aren’t forced for choices. They’re only if you want to. So that’s why I only put them on ones I want to save and not on ones that lost progress doesn’t matter on. Specifically they’re good for ones that have the jump command returning you to a page or label so that your save will be on the page or label when you reload. It’s disorienting if it doesn’t do that.

IE9 has a miniscule market share (https://www.w3counter.com/trends) and I suspect these are only business computers running legacy apps that require it. You should really consider dropping support for it. ES6 has many awesome features. I think it’s a fair tradeoff.