deprecated twine page

Twine: either(), a random picker function

Update: This script is now built into Twine 1.4! It is no longer necessary to install it.

This is a very short script that allows you to use a function called "either" in Twine's <<set>>, <<print>> and <<if>> macros. Give it several string or number values, separated by commas, and it will pick one of them randomly to use in the macro.

Obsolete script removed: use Twine 1.4

This functions largely identically to the Game Maker choose() function. You can use it like this:

<<print either("a dusty glade", "a sinister vale", "a murky gully", "a desolate gulch") >> - Prints one of the strings, chosen randomly.

<<set $value = either(0,1,2,3,4)>> - Sets $value to one of the numbers.

<<if either(true, false)>> - This is true 50% of the time. Equivalent to <<if Math.random() lt 0.5>>

In the first case, you can see it's functionally similar to the <<rnd>> macro - but, as demonstrated, it can also be used inside <<set>> as well as <<print>>.

If you patch the <<display>> macro, you can also use it to display a random passage, serving as a more primitive kind of <<randomp>>:

<<display either("Cellar", "Garden", "Observatory")>>

Feel free to report any bugs to @webbedspace.

Twine: <<if>> and whitespace

Update: This behaviour has been changed in Twine 1.4! It no longer applies.

This is just a summary of how <<if>>, by default, handles whitespace characters (that is, line breaks, spaces, tabs, and such.)

The behaviour

The rule is: the <<if>> macro removes all whitespace contained between the <<if>>, <<endif>> or <<else>> tags and any non-whitespace contained text.

Consider this code sample, in which the line breaks are marked:

The magenta line breaks (those contained between the <<if>> and <<endif>> and the actual text) will be removed by the macro. Thus, this will render as follows:

A slice of marmalade toast on a plate,
a bit of quiet,
and a spot of tea.

A slice of marmalade toast 
on a plate,
a bit of quiet,
and a spot of tea.

Resisting this behaviour

The <<if>> macro was primarily designed for inserting whole paragraphs. This behaviour is, however, less useful for, say, inserting sentence fragments into paragraphs, which often have leading spaces. There are a few ways to get around this. Such as:

  • Empty comment syntax

    If you pad the interiors of <<if>> macros with an empty comment tag "/%%/", then the behaviour will be overridden, because the comment tags will be treated as text despite not appearing in the final story.

    First line. <<if $a gt 0>>/%%/
    Second line.
    /%%/<<endif>>
    This might be the most basic method of all listed here.

  • Using <<print>>
    The <<print>> macro can also be used.

    A complete sentence<<if $a gt 0>>
    <<print " with an extra amendment">>
    <<endif>>.

    However, it isn't obvious how to add line breaks using this method. There is a way, obscure though it is:

    <<if $a gt 0>><<print "First line." + String.fromCharCode(13) + "Second line.">>
    <<endif>>

  • Using a HTML <br>

    If you want to preserve a line break inside <<if>>, you can do so by the forceful method of using inline HTML.

    First line.<<if $a gt 0>>
    <br>Second line.
    <<endif>>
    

    [*]Patching <<if>> to not remove whitespace

    Much like most undesirable Twine behaviour, this can be patched out on a story-by-story basis. The script code for doing so is as follows... however, using this will conflict with my <<else if>> script, so you can't have one and the other.

    version.extensions.ifMacros={major:1,minor:1,revision:0};macros["if"]={handler:function(place,macroName,params,parser){
    var conditions=[],clauses=[],srcOffset=parser.source.indexOf(">>",parser.matchStart)+2,src=parser.source.slice(srcOffset),endPos=-1,currentCond=parser.fullArgs(),currentClause="",t=0,nesting=0;
    for(var i=0;i<src.length;i++){if(src.substr(i,9)=="<<endif>>"){nesting--;if(nesting<0){endPos=srcOffset+i+9;
    conditions.push(currentCond);clauses.push(currentClause);break;}}if((src.substr(i,6)=="<<else")&&nesting==0){conditions.push(currentCond);
    clauses.push(currentClause);currentClause="";t=src.indexOf(">>",i+6);if(src.substr(i+6,4)==" if "){currentCond=Wikifier.parse(src.slice(i+10,t));
    }else{currentCond="true";}i=t+2;}if(src.substr(i,5)=="<<if "){nesting++;}currentClause+=src.charAt(i);
    }try{if(endPos!=-1){parser.nextMatch=endPos;for(i=0;i<clauses.length;i++){if(eval(conditions.shift())){new Wikifier(place,clauses[i ]);
    break;}}}else{throwError(place,"can't find matching endif");}}catch(e){throwError(place,"bad condition: "+e.message);
    }}};

    The future
    The next version of Twine 1 may change the behaviour of <<if>> to not remove whitespace. I'm still debating how or whether this should be enabled, and how to enable current story code to continue to function correctly.

  • Twine: mix HTML tags and Twine markup syntax

    Update: This script is now built into Twine 1.4! It is no longer necessary to install it.

    Just now I was thinking to myself, "Why is it that inline HTML must be stuck between the <html> tags in Twine, anyway? Would it be possible to make HTML usable intersperced with Twine syntax?" So I decided to give it a try. The resulting Javascript code is as follows:

    Obsolete script removed: use Twine 1.4

    This code will allow you to put HTML tags in Twine passage text, and have them function in the compiled game. Note: it requires all HTML tags to be properly closed, with the exception of the so-called "void tags" that never have a closing pair (br, hr, area, img, input, embed, param, source, track). Unmatched end tags will be ignored!

    Feel free to report any bugs to @webbedspace.

    Twine: apply CSS to individual characters

    Update: The Javascript on this page is now built into Twine 1.4! It is no longer necessary to install it.

    This script causes every single character in passages to be wrapped in a <span class="char"> element. This enables a number of mildly interesting CSS effects.

    Obsolete script removed: use Twine 1.4

    These characters have the class of "char", and also a class equal to themselves ("a" for the letter "a", "2" for "2", etc.) It's recommended that you use the :nth-child pseudo-class to select them. Some potential CSS effects that can be performed include the following (examples only):

    Horizontally spin characters on mouseover:
    (works best with large text)

    .char:not(.space):hover {
      transform: rotateY(1440deg);
      -webkit-transform: rotateY(1440deg);
    }
    .char:not(.space) {
      display: inline-block;
      transition: transform 2s ease-out;
      -webkit-transition: -webkit-transform 2s ease-out;
    }
    

    Wavy text:

     .char{ position:relative; }
    .char:nth-child(8n) { top:0px; }
    .char:nth-child(8n+1) { top:-1px; }
    .char:nth-child(8n+2) { top:-1.5px; }
    .char:nth-child(8n+3) { top:-1px; }
    .char:nth-child(8n+4) { top:-0px; }
    .char:nth-child(8n+5) { top: 1px; }
    .char:nth-child(8n+6) { top: 1.5px; }
    .char:nth-child(8n+7) { top: 1px; }

    Animated wavy text:

    .passage {
      font-size: 3em;
    }
    .char { 
      position:relative;
    }
    .char:nth-child(8n) { 
      animation: wavetext 4s 0s infinite;
      -webkit-animation: wavetext 4s 0s infinite;
    }
    .char:nth-child(8n+1) { 
      animation: wavetext 4s -0.5s infinite;
      -webkit-animation: wavetext 4s -0.5s infinite;
    }
    .char:nth-child(8n+2) { 
      animation: wavetext 4s -1s infinite;
      -webkit-animation: wavetext 4s -1s infinite;
    }
    .char:nth-child(8n+3) { 
      animation: wavetext 4s -1.5s infinite;
      -webkit-animation: wavetext 4s -1.5s infinite;
    }
    .char:nth-child(8n+4) { 
      animation: wavetext 4s -2s infinite;
      -webkit-animation: wavetext 4s -2s infinite;
    }
    .char:nth-child(8n+5) { 
      animation: wavetext 4s -2.5s infinite;
      -webkit-animation: wavetext 4s -2.5s infinite;
    }
    .char:nth-child(8n+6) { 
      animation: wavetext 4s -3s infinite;
      -webkit-animation: wavetext 4s -3s infinite;
    }
    .char:nth-child(8n+7) { 
      animation: wavetext 4s -3.5s infinite;
      -webkit-animation: wavetext 4s -3.5s infinite;
    }
    @keyframes wavetext {
      0%, 100% { top: 0em; } 50% { top: 0.5em; }
    }
    @-webkit-keyframes wavetext {
      0%, 100% { top: 0em; } 50% { top: 0.5em; }
    }

    Rapid rainbow text:

    .char:nth-child(8n) { color:hsl(45,100%,75%); }
    .char:nth-child(8n+1) {color:hsl(90,100%,75%); }
    .char:nth-child(8n+2) {color:hsl(135,100%,75%); }
    .char:nth-child(8n+3) {color:hsl(180,100%,75%); }
    .char:nth-child(8n+4) {color:hsl(225,100%,75%); }
    .char:nth-child(8n+5) {color:hsl(270,100%,75%); }
    .char:nth-child(8n+6) {color:hsl(315,100%,75%); }
    .char:nth-child(8n+7) {color:hsl(0,100%,75%); }

    Illuminate letters on mouseover

    .char { 
      transition: all 5s; -webkit-transition: all 5s;
      opacity: 0.4;
    }
    .char:hover {
      transition: all 0.1s; -webkit-transition: all 0.1s;
      opacity:1;
      text-shadow: 0 0 1em white;
    }
    

    Erase text on mouseover

    .char { 
      transition: opacity 999s step-end; -webkit-transition: opacity 999s step-end;
    }
    .char:hover {
      opacity:0;
      transition: opacity 1ms; -webkit-transition: opacity 1ms;
    }
    

    Remove all the T's in the passage text:
    .char.t {
      display:none;
    }

    Change "u" to "U":

    .char.u {
      visibility:hidden;
    }
    .char.u::before {
      content: "U";
      position:absolute;
      visibility:visible;
    }

    These are to be considered basic examples - prompts for more practical uses.

    This code also enables some particularly interesting Javascript visual effects to be performed, which I shall explore in a future blog post.

    Feel free to report any bugs to @webbedspace.

    Twine: Improved back and forward buttons in Sugarcane

    Update: this feature is now built into Twine 1.4! This behaviour is now the default and this page is no longer necessary.

    This script allows Sugarcane to use HTML5 history management instead of URL hash strings to alter the browser history. This means that various non-deterministic game state changes (random numbers, player data input, state changes inside <<replace>> macros, etc.) will be properly remembered when you use the browser's Back button. This code also updates <<back>> and <<return>> and the Rewind menu.

    Obsolete script removed: use Twine 1.4

    Feel free to report any bugs to @webbedspace.

    Twine: Jonah: Disable all links after leaving a passage

    Update: The Javascript on this page is now built into Twine 1.4! It is no longer necessary to install it. You can enable this in Twine 1.4 by setting "Undo: off" in the StorySettings passage.

    If you're using Jonah, and you're not using single-passage, then you may find this useful. It causes all internal links to behave like the <<choice>> macro.

    Obsolete script removed: use Twine 1.4

    This is designed to also work with approximately all of my link macros (<<cyclinglink>>, <<revise>>, <<replace>> etc.)

    Version history:

    1. 9-5-13: Initial.

    Feel free to report any bugs to @webbedspace.

    Twine macro: <<else if>>, an improvement to <<if>>

    Update: This macro is now built into Twine 1.4! It is no longer necessary to install it.

    This is not strictly a new macro, but a replacement version of the <<if>> macro. You might have noticed that nesting the <<if>> macro can get a bit messy:

    <<if $fire eq "warm">>
    The fire isn't hot yet.
    <<else>>
    <<if $medal eq "water">>
    You medal protects you.
    <<else>>
    <<if $dead gt 0>>
    Not again!
    <<else>>
    You died!
    <<endif>>
    <<endif>>
    <<endif>>

    The <<else if>> macro allows you to essentially have multiple <<if>> macros chained together, without needing to nest each of them within the <<else>> / <<endif>> tag pair of the other:

    <<if $fire eq "warm">>
    The fire isn't hot yet.
    <<else if $medal eq "water">>
    You medal protects you.
    <<else if $dead gt 0>>
    Not again!
    <<else>>
    You died!
    <<endif>>
    

    The macro code is here:
    Obsolete script removed: use Twine 1.4

    You might wonder why I chose "else if" instead of "elseif"... I guess my mind flipped a coin and the 'natural language' side shone upward.

    If someone else has already developed a similar macro, as I suspect they might, then do tell.

    Feel free to report any bugs to @webbedspace.

    Twine: visited(), a function to find how often a passage was visited

    Update: The Javascript on this page is now built into Twine 1.4! It is no longer necessary to install it.

    I've noticed that a good deal of variable usage in Twine is simply employed to track whether or not you've visited a certain passage previously, or done so a number of times. This script code provides you with a function that can give this value straight, without needing to use the <<set>> macro.

    Obsolete script removed: use Twine 1.4

    To use it, use "visited("Passage")" as a value in a <<set>>, <<if>> or <<print>> macro. It will equal the number of times you've visited the passage whose name is between the quotes and parentheses. For instance:

    1. <<if visited("Store")>>You've been to the store.<<endif>>
    2. <<if not visited("Firepit") and not visited("Maggotpit")>>You've not yet been to the fire pit or maggot pit.<<endif>>
    3. <<if visited("Trap Floor") gt 2>>Your repeated stomping is making the floor weaken.<<endif>>
    4. You've visited the chasm <<print visited("Dark Chasm")>> times.<<endif>>
    5. <<set $count = visited("Jewelers")>>You've visited the Jeweler's <<print $count>> times.<<endif>>

    Also, instead of a passage name, you can put a variable.
    1. <<if visited($bossPassage)>>If only you'd had this when you'd fought the boss!<<endif>>
    2. <<set $beenToMyHome = visited($myHome)>>

    New: If you just put visited(), then it will count visits to the current passage, without you needing to include the passage's name.
    <<set $beenHereBefore = visited()>>

    In addition, here is a function called "visitedbefore" which can be used to tell you if a passage has ever, in the course of the game, been visited before another passage.

    window.visitedbefore = function(e,f) {
    	var h,c,ret;
      	for(ret=c=0; c<state.history.length; c++) {
    		h = state.history[c].passage;
    		if(h && h.title == e) {
    			return true;
    		} else if (h && h.title == f) {
    			return false;
    		}
    	}
    	return false;
    }

    This only returns true or false. Use it like so:
    1. <<if visitedbefore("Firepit", "Maggotpit")>>First the fire pit, then the maggot pit, now this?!<<endif>>
    2. <<if visited("Armory") and visitedbefore("Firepit", "Maggotpit")>>First the fire pit, then the maggot pit, now this?!<<endif>>

    Again, variables can be substituted for passage name strings:
    1. <<if visitedbefore("Dragon Hoard", $myHome)>>You must've left it at home after meeting the dragon.<<endif>>

    The two passage names must be separated by a comma.

    Some notes:

    1. This doesn't count use of the <<display>> macro as a visit.
    2. If neither passage has been visited, visitedbefore() will return false.

    Feel free to report any bugs to @webbedspace.

    Twine: escape line breaks with backslashes

    Update: The Javascript on this page is now built into Twine 1.4! It is no longer necessary to install it.

    As you know, one of the most prominent difficulties with mixing code and prose in Twine games is the handling of line breaks. If you have a bunch of macros interspersed in your prose, especially <<if>> macros, then the temptation is to leave them on separate lines from the text, for readability. However, the line breaks adjacent to these macros end up appearing in the rendered passage.

    The <<silently>> macro is the 'official' solution...

    <<silently>>
    <<set $hp -= 1>>
    <<set $dead = true>>
    <<endsilently>>
    
    Wrap up all of the macros in it and the line breaks will be suppressed. But, it seems to me to be a half-effective solution, requiring two large macro tags enclosing each block, and disallowing <<print>> macros within it.

    I've previously advocated a different solution, involving the TiddlyWiki comment syntax:

    /%
    %/<<set $hp -= 1>>/%
    %/<<set $dead = true>>/%
    %/
    
    This has the advantage of allowing the use of <<print>> macros. But it, too, is authorially and visually unwieldy, requiring both the end and beginning of lines to be tagged with one of two similar-looking tags.

    However, I believe it's on the right track. Right now, my proposal is to directly borrow the syntax of the C language's preprocessor - a single backslash at the end of a line suppresses the line break:

    <<set $hp -= 1>>\
    <<set $dead = true>>\
    You are \
    <<if $hp gt 1>>
    hale
    <<else>>
    almost dead
    <<endif>>.
    
    In my opinion, this has much more convenience compared to the other two.
    Here is the script code to enable this in your stories:

    Obsolete script removed: use Twine 1.4

    To recap, when you install this script:

    1. A line that reads:
      You have \
      twenty dollars.
      ...will be rendered as "You have twenty dollars."
    2. A line that reads:
      You have \
      <<set $cash = "twenty">>\
      <<print $cash>>\
      dollars.
      ...will be rendered as "You have twenty dollars."

    Feel free to report any bugs to @webbedspace.

    Twine: loading jQuery without editing the HTML

    Update: Twine 1.4 now lets you load jQuery via a StorySettings option, so this page is no longer necessary.

    You can load jQuery into your Twine game via Google Hosted Libraries using this simple script code:

    Obsolete script removed: use Twine 1.4

    Change whatever you want the function passed to ready() to do.
    Notes:
    * Since Twine's engine uses '$' for something, jQuery must sadly relinquish that convenient abbreviation.
    * Due to a minor bug, Twine's runtime currently doesn't work at all in IE 8 or below, so there's not much reason to use jQuery 1.9 over 2.0
    * Using Google Hosted Libraries of course means that people can't play your game without a live internet connection - which, in the extremely rare chance you intend your Twine game to be available offline, should be borne in mind. Nevertheless, it can be more convenient for you, the author, due to not having to provide the jQuery file yourself.
    Feel free to report any bugs to @webbedspace.

    Syndicate content