+1 vote
by (1.2k points)

I've been bashing away at a game and one of the features I'm trying to implement involves running some JavaScript on the passage after it has rendered. I'm working with Sugarcube 2.20.0 and Twine 2.1.3.

The function I've got takes the contents of the passage class div, messes with the text strings (but preserves the HTML tags), and then outputs back into the same div. I've got it running outside Twine in a browser but there it's activated by pressing a button on a page (so the whole not running until the page is rendered thing isn't an issue there).

The general goal for this code is to have it activated for every passage (it'll then read the relevant State.variables variable and run based on that).

What I don't know is how best to accomplish that. I've tried to read the documentation but I'm unable to progress.
It's not clear whether the code should sit in one of the special passages, in a <<script>> block somewhere (a special passage?), the Story JavaScript area, or somewhere else. Or some combination of those things?

I suppose the other thing I need is to know whether this is how I should be reading/writing the passage contents?


On a normal webpage that gives me the contents of a div but it seems that's not the way to do it in Sugarcube.

Hopefully that's enough to go on for someone to point me in the right direction. I'm learning JavaScript bit by bit and documenting like a lunatic to help myself understand but there's enormous gaps in my knowledge.

1 Answer

+1 vote
by (8.6k points)
edited by

The special passages are almost the right place, but the task objects might be more suitable here - specifically the postrender and postdisplay ones. In case of postrender, you can get the HTML element that will be displayed as the first argument of your function, so you don't even need to search for it in the page.

postrender["text transformation"] = function(content, taskname) {
     * Your text transformations on content go here;
     * taskname will be "text transformation" in case you ever want
     * to re-use one function in multiple places

The alternative (in SugarCube 2.20.0 and above) is the ":passagerender" event, which functions about the same.

jQuery(document).on(':passagerender', function(e) {
    const content = e.content;
    /* Do with the "content" variable as you would in the example above */


by (1.2k points)

I read your post.  I don't see how the situations are comparable.  Even if you didn't understand why I said, "you can't do that" (paraphrased), the understanding that you can't do that should have clear enough.  YMMV.  /shrug

Just trying to make a joke. I suspect you'd hate the deliberately jovial way I comment my code!

 I never said what you want to do was infeasible, in general, simply that you can't do it in the manner you're attempting.

From what I can tell what you're saying is in essence that my knowledge of how this whole setup works is hilariously primitive and in order for my script to work I would need to do extensive reading to even begin to understand the underlying concepts, let alone mess with them. 

That's not a criticism - it's useful insight for me. I'm re-evaluating the cost/benefit of the task.

This script was going to be a fun feature for me to mess about with - it's not actually important to my game. I try not to add things to my game that I don't personally understand and in order to understand how to manipulate arbitrary text without breaking the invisible magic (as to me it might as well be!) of the web page the work just isn't worth it. That is to say your demonstration of knowledge shows me that what I want to do is unfeasible given the other elements in the equation.

I'm better off putting that time and energy towards the parts of my game's code that I already understand. Thank you for helping me see that.

by (68.6k points)
edited by

I think I may have snuck an edit by you as you were replying.

No worries, I don't have a problem with levity.  I'll simply note that it can be hard to determine when someone's joking and when they're being an arse when all you have to go by is text.  Smilies used to help, then jerks started using them to be cute, so their use is no longer a good gauge of intent.

Suffice it to say that your attempt at humor slipped right by me, which is probably my fault, as I tend to be fairly literal.  Mea culpa. blush

I won't lie, to correctly do what you want to do is not a beginner topic.  If you wanted to treat it as a learning experience, however, it is doable.

Regardless.  Good luck, and we'll be here if you need help.

by (1.2k points)

In my younger days I was quite the fighter online - these days I try to be a bit more mellow laugh I was only trying to call on you to have mercy because of my feeble understanding. There's no end to what I don't know!

Anyway I didn't bother posting the code because it basically does some letter addition and shuffling at the moment. Pretty simple stuff that might as well be "hello world" to a coder like yourself. If it worked I was going to then play with it and see about adding other fun things like doubling up on some words (so a sentence like "You know what the queue is usually like." might become "You-you know what the queue is usually like.").

It's interesting to learn how these things fit together and I'm having a lot of fun putting my first game together. I wanted to learn to program as a teenager and couldn't but over the last couple of years I've been enjoying it and understanding in a way I didn't back then.

I won't lie, to correctly do what you want to do would is not a beginner topic.  If you want to treat it as a learning experience, however, it is doable.

Good to know that the idea is doable. I might give it a go if I have an obsessive late night. Chances are I'll fail but perhaps with enough attempts I'll get it.

On the plus side the code works and the execution works. It's just the invisible magic that I hadn't accounted for that is the problem. When I started neither the code nor the execution worked!

Is there any kind of primer on what I need to know to get this to work? Something you could recommend as a spot of bedtime reading? smiley

by (8.6k points)

Here's a little trick which might get you going in the right direction. Using jQuery:

  • find all the non-text child nodes of the content
  • add the content one back to that list
  • get all the content (including the child nodes) into one big list
  • filter this list for just the text nodes (nodeType === Node.TEXT_NODE)
  • for each of the text node, run your text replacement on its nodeValue (the actual text)

In code:

postrender["text transformation"] = function(content, taskname) {
        .filter(function() { return this.nodeType === Node.TEXT_NODE; })
        .each(function() { this.nodeValue = this.nodeValue.replace(/o/g, "รถ"); });

by (1.2k points)

I wrote something using JavaScript eventually. It grabs the nodelist for the main passage and then makes a list of index values of nodes fitting the relevant criteria. It then modifies only the .textContent properties of those whilst leaving the other bits of cleverness intact.

This not only preserves links but also means that the drunkify text transformation only applies to relevant text. Things with other tags (such as dialogue from other characters) remains unaffected. Brilliant!

The special PassageDone passage checks whether the player character's sobriety level is low enough and includes the drunkify script if appropriate. That script looks like this:


$(document).on(':passagedisplay', function (ev) {