Saturday, 29 March 2014

Composite images: Superhero


This is a "proof of concent"; there is no game, just the character creation steps for creating your own superhero. In one step you get to choose your outfit, and then see an image of it.

Try it out

It has a limited number of images that it puts on top of each other to give the final effect (so six images of capes for guys, six for girls, etc.).

It uses CSS to superimpose the images. Each image goes into its own <div>, which has an absolute position set. Sounds complicated, but the CSS code is trivial:

div.abs-box { 
    position: absolute; 
    top: 200px; 
    right: 0px; 
}

The basic Twine code looks like this - just a normal img tag inside a <div>, with the class set to correspond to the CSS already set. You can have as many of these on the page as you like; they will all get draw on top of each other.

<div class="abs-box">[img[m_red_cape]]</div>

However, what I did was set up a macro to handle it.

macros.pic = {
  handler: function(place, macroName, params, parser) {
    var s = '';
    if (sex) {
      if (cape != "") {
        s+= '<div class="abs-box">[img[f_' + cape + '_cape]]</div>';
      }
      s+= '<div class="abs-box">[img[f_' + suit + '_suit]]</div>';
      s+= '<div class="abs-box">[img[f_' + swimsuit + '_swimsuit]]</div>';
      s+= '<div class="abs-box">[img[f_' + belt + '_belt]]</div>';
      s+= '<div class="abs-box">[img[f_' + col + ']]</div>';
    } else {
      if (state.active.variables.cape != "") {
        s+= '<div class="abs-box">[img[m_' + cape + '_cape]]</div>';
      }
      s+= '<div class="abs-box">[img[m_' + suit + '_suit]]</div>';
      s+= '<div class="abs-box">[img[m_' + underwear + '_uw]]</div>';
      s+= '<div class="abs-box">[img[m_' + belt + '_belt]]</div>';
      s+= '<div class="abs-box">[img[m_' + col + ']]</div>';
    }
    new Wikifier(place, s);
  },
}; 

It looks complicated, but all it is doing is building up a string, s, with a series of those img tags, then adding the string to the passage.

Then you need the images. I used PNG format, as it supports transparency, and edited it with GIMP. I started with two images, one for a guy, one for a girl. For each, I changed the cape colour using the colourise feature, saving each colour, then I erased the cape, and did the bodysuit, and so on.


Friday, 28 March 2014

The "tale" variable

This is following on from the post on the "state" variable. My start page looks like this, and there are two other pages.

<<set $x = 4>>
<<set alert(JSON.stringify(tale))>>
<<set $y = 7>>
[[Two]]

In Sugarcube I get this (after a little formatting):

{
"passages":{
  "Three":{"title":"Three","id":0,"domId":"passage-three","text":"[[Start]]","textExcerpt":"Start…","tags":[],"classes":[],"className":""},
  "Two":{"title":"Two","id":1,"domId":"passage-two","text":"A second page.\n<<set $z = 10>>\n[[Three]]","textExcerpt":"A second page. Three…","tags":[],"classes":[],"className":""},
  "Start":{"title":"Start","id":2,"domId":"passage-start","text":"A first page.\n\n<<set $x = 4>>\n<<set alert(JSON.stringify(tale))>>\n<<set $y = 7>>\n[[Two]]","textExcerpt":"A first page. Two…","tags":[],"classes":[],"className":""},
  "StoryTitle":{"title":"StoryTitle","id":3,"domId":"passage-storytitle","text":"Untitled Story","textExcerpt":"Untitled Story…","tags":[],"classes":[],"className":""},
  "StoryAuthor":{"title":"StoryAuthor","id":5,"domId":"passage-storyauthor","text":"Anonymous","textExcerpt":"Anonymous…","tags":[],"classes":[],"className":""}
},
"title":"Untitled Story",
"domId":"untitled-story"
}


So tale has three properties, passages (a hash table of all the passages), title and domId (a computer friendly - slugified - form of the title). Each member of the passages hash table looks like this, after formating:


"Three":{
  "title":"Three",
  "id":0,
  "domId":"passage-three",
  "text":"[[Start]]",
  "textExcerpt":"Start…",
  "tags":[],
  "classes":[],
  "className":""
},

Some of this obvious; text is the text, tags is an array of tags, title is the name of the passage (and this is also used as the key for the hash table). The id property looks to be a unique number for each passage in the game. After that, I do not know.

In Sugarcane it looks like this:


{
"storysettings":{},
"passages":{
  "Three":{"title":"Three","id":0,"tags":[],"text":"[[Start]]"},
  "Two":{"title":"Two","id":1,"tags":[],"text":"A second page.\n<<set $z = 10>>\n[[Three]]"},
  "Start":{"title":"Start","id":2,"tags":[],"text":"A first page.\n\n<<set $x = 4>>\n<<set alert(JSON.stringify(tale))>>\n<<set $y = 7>>\n[[Two]]"},
  "StoryTitle":{"title":"StoryTitle","id":3,"tags":[],"text":"Untitled Story"},
  "StoryAuthor":{"title":"StoryAuthor","id":5,"tags":[],"text":"Anonymous"}
}
}

So we just have two properties; storysettings and passages. Passages is again a hash table, and each entry has a title string, a tags array, a text string and an id number. All the esoteric properties are missing.

Tale has a number of methods. This is what I found in Sugarcube.


setTitle: Sets the title (and domId).
has: Returns true if the story has a passage with the given name or id.
get: Returns the passage from the given name or id.
lookup:  Returns an array of passages. As far as I can see, this can only be used for looking up tags, though it is coded to handle any array property of a passage. The first parameter should be "tags", the second the tag to look for. An optional third parameter will have the array sorted by the given property (defaults to title).
reset: Resets the passage.

Here is an example of using lookup. It gets an array of passages with the tag "testtag", sorted by title, and pops up a message box with the name of the first.

ary = tale.lookup("tags", "testtag");
alert(ary[0].title);

Wednesday, 26 March 2014

The "state" variable

There is a useful JavaScript trick that will print all the properties of a variable. It might not work on all Browsers, but is good for experimenting with. In this example, it will give an alert with all the properties of the state.active variable. This is using Sugarcube.


By the way, it is safer to do this as an alert rather than putting the result in text as the text may be part of the variable we are looking at, and you can potentially get your browser stuck in a loop.

Also, I am going to refer to these things as variables; in strict object-orientated language they are properties.

So let us start by looking at state.active, like is:

<<set alert(JSON.stringify(state.active))>>

I set up a basic game, with this is the Start passage:

<<set $x = 4>>
<<set alert(JSON.stringify(state
.active))>>
<<set $y = 7>>


And I got this:

{"title":"Start","variables":{"x":4},"sidx":0} 

So I can see that state.active.title contains the name of the current passage, and state.active.variables contains all the variables currently in use (in this case just $x had been set).

We can take that back a step and look at state. I am going to add a link to a second passage, that links to a third passage, which links back here, and the second page also sets $z to 10:

<<set $x = 4>><<print JSON.stringify(state)>>
<<set $y = 7>>
[[Main]]


Now we see this:

{"active":{"title":"Start","variables":{"x":4,"y":7,"z":10},"sidx":3},"history":[{"title":"Start","variables":{},"sidx":0},{"title":"Two","variables":{"x":4,"y":7},"sidx":1},{"title":"Three","variables":{"x":4,"y":7,"z":10},"sidx":2},{"title":"Start","variables":{"x":4,"y":7,"z":10},"sidx":3}],"suid":"3bb64cef-573d-43d3-ad06-517b0d046bc5"}

So we can see that state.active holds the current values, while state.history contains an array of historic values of each page visited in order.

Let us see what Sugarcane does.:


{"history":[{"passage":{"title":"Start","id":2,"tags":[],"text":"A first page.\n\n<<set $x = 4>>\n<<set alert(JSON.stringify(state))>>\n<<set $y = 7>>\n[[Two]]"},"variables":{"x":4,"y":7,"z":10}},{"passage":{"title":"Three","id":0,"tags":[],"text":"[[Start]]"},"variables":{"x":4,"y":7,"z":10}},{"passage":{"title":"Two","id":1,"tags":[],"text":"A second page.\n<<set $z = 10>>\n[[Three]]"},"variables":{"x":4,"y":7,"z":10}},{"passage":{"title":"Start","id":2,"tags":[],"text":"A first page.\n\n<<set $x = 4>>\n<<set alert(JSON.stringify(state))>>\n<<set $y = 7>>\n[[Two]]"},"variables":{"x":4,"y":7}},{"passage":null,"variables":{}}],"id":"1395737963066"}

Sugarcane is rather different. It has no active variable. Its history variable holds an array just like Sugarcube, but in reverse order, so for Sugarcane, state.history[0] is always the current page, for Sugarcube it is the first page.

Sugarcane also has a passage variable, and the passage title is in there, as well as the tags and the whole passage text. As you can see this means a lot more data being stored.

Tuesday, 25 March 2014

Macros with clauses

So what if I want some text to be displayed only when the player first sees a passage and optionally other text displayed on subsequent visits? We could use if/else/endif with the visited function, but let us see if we can create a macro to do. The passage might look like this:

Hi there. <<first>>
First time here, eh?
<<notfirst>>
Back again already?
<<endfirst>> What are you doing?


The principle is not unlike if/else/endif, and so I created a macro based on that code. I have heavily commented it, so if you have at least a little familiarity with code you could adapt it to other uses (for example, to handle male and female characters).

Note that this is for Sugarcube. For Sugarcane (and I guess other formats), you need to comment out one line, and uncomment another, towards the end - it says exactly which in the code.

macros['first'] = {
  handler: function (place, macroName, params, parser) {
    //var condition = parser.fullArgs();
    // This is the place in the text that the first clause starts
    var srcOffset = parser.source.indexOf('>>', parser.matchStart) + 2;
    // This is the source text, with everything up to the start
    // of the first clause chopped off
    var src = parser.source.slice(srcOffset);
    var endPos = -1;
    var firstClause = '';
    var notClause = '';

    // First we need to scan through the text to extract the first and second clauses.
    for (var i = 0, currentClause = true; i < src.length; i++) {
      // Have we reached the end of the second clause?
      // If so, set endPos (the start of the rest of the text)
      // and terminate the loop
      // The number 12 is the the length of <<endfirst>>
      if (src.substr(i, 12) == '<<endfirst>>') {
        endPos = srcOffset + i + 12;
        break;
      }

     
      // Have we reached the end of the first clause?
      // If so, flip to doing the second clause
      // and jump ahead to skip the <<notfirst>> characters.
      // The number 12 is the the length of <<notfirst>>
      if (src.substr(i, 12) == '<<notfirst>>') {
        currentClause = false;
        i += 12;
      }

      // We are currently in the middle of one of the clauses, so this character
      // should be added to whichever clause we are on.
      if (currentClause) {
        firstClause += src.charAt(i);
      } else {
        notClause += src.charAt(i);
      }
    };

    // this could go wrong, so get ready to catch errors!
    try {

      // Now we want to display the correct clause
      // Note that text instead the clause is trimmed to remove
      // line breaks and spaces at either end
      // This is for Sugarcube, if you are using sugarcane, comment the next line, and uncomment the one after
      if (visited(state.active.title) == 1) {
      // if (visited(state.history[0].passage.title) == 1) {
        new Wikifier(place, firstClause.trim());
      } else {
        new Wikifier(place, notClause.trim());
      }

      // Finally push the parser past the entire expression
      if (endPos != -1) {
        parser.nextMatch = endPos;
      } else {
        throwError(place, "can't find matching endfirst");
      }

    // Oh, and just in case there was a problem, catch those errors
    } catch (e) {
      throwError(place, 'bad condition: ' + e.message);
    };
  }
};


// Also need to register <<notfirst>> and <<endfirst>> so Twine does
// not throw an error when it hits them.
macros['notfirst'] = macros['endfirst'] = { handler: function() {} };

Monday, 24 March 2014

Strategy games

Twine has the potential for creating strategy games; this is a quick guide to getting started. Strategy games can be as complicated as you like, we will build a very basic one involving running a small kingdom.

This is written for SugarCube, but should work in other formats too.

Variables

A strategy game is going to involve a lot of variables, tracking the game state. There are 2 ways to handle a variable in twine. Say we have a variable for tracking monery called $gold, we could set it and print it like this:

<<set $gold = 100>>
<<print $gold>>


If you want to change the value in a function, you might do this (in Sugarcube):

state.active.variables.gold += 50;

We are going to be doing a lot of coding, and I would really rather not have to type "state.active.variables." every time, so for this game we will be using variables that are local to the script passage. Setting the value and modifying might look like this:

var gold = 100;
gold += 50;


Note that you do need to declare it before it is used, so the "var" is necessary (that said, Sugarcane seems not to mind if it is missed sometimes).

To display the value, you then need a macro or a function, which is not so convenient, but only has to be set up once.

macros.gold = {
  handler: function(place, macroName, params, parser) {
    new Wikifier(place, "" + gold);
  },
};


Note that Wikifier expects a string; if I sent it just a number, nothing would get added. By adding the gold to an empty string first, the number is converted to a string, and gets displayed properly.

In fact, a better way to handle this is a macro that displays all the important statistics:

macros.summary = {
  handler: function(place, macroName, params, parser) {
    var s = '';
    s += "Turn: " + turn;
    s += "\nKnights: " + knights;
    new Wikifier(place, s);
  },
};


Passages

We will have relatively few passages. A "Start" to introduce the game will lead to the "Main" passage, and this will have links to a number of passages where the player can control different aspects of the game. A link to this very page for moving on time, and a link to a help page will be about it. All the real work will be done in JavaScript.

Here is the "Start" passage:

<<set init()>>Manage the Kingdom of Gathnia, lead it to properity.

[[Main]]


Besides the introductory text, there is a macro that invokes a function called "init", and a link to the "Main" passage. We will get back to the "init" function in a bit.

The "Main" passage looks like this:

<<summary>>

[[Manage Knights]]
[[Next Turn|Main][nextTurn()]]


So far all it does is give a summary report, then give a link to the knight management section, and a link to this page. Linking back to the same page is something we will do a lot. Note that the link calls a function, "nextTurn".

Here is the passage for managing knights; the same sort of thing.

[[Recruit|Manage Knights][recruitKnight()]]
[[Main]]


Strategy games are great for competitions that limit the word and passage count!


The Script

Here is the script passage, where the variables are declared and the three functions are defined:

var turn, knights;

window.init = function() {
  turn = 0;
  knights = 0;
}

window.nextTurn = function() {
  turn++;
}

window.recruitKnight = function() {
   knights++;
}


The "init" function sets up the initial values (it is best to do this in a function and call the function so they get re-initialised on a restart). The other two functions increase the value of a certain variable by one.

You should now have a working game where you can increase the turn and recruit knights. And nothing else.

Peasants and gold

Let us introduce peasants and gold. Pretty much any strategy game will use some form of money as it is a way of limiting what the player can do and measuring success. Let us say it costs 50 gold to recruit a knight, and you have to pay them 20 gold each turn. However, peasants make you 2 gold each turn. Here are some updated functions to reflect that.

var turn, knights, peasants, gold;

window.init = function() {
  turn = 0;
  knights = 0;
  gold = 100;
  peasants = 50;
}

window.nextTurn = function() {
  turn++;
  gold += (state.active.variables.peasants * 2);
  gold -= (state.active.variables.knights * 20);
}

window.recruitKnight = function() {
  knights++;
  gold -= 50;
}

macros.summary = {
  handler: function(place, macroName, params, parser) {
    var s = '';
    s += "Turn: " + turn;
    s += "\nKnights: " + knights;
    s += "\nPeasants: " + peasants;
    s += "\nGold: " + gold;
    new Wikifier(place, s);
  },
};


Bandits!

Wait... What is the point of recruiting knights? They cost money, so they need to have value. Let us say that knights protect the land from bandits and also give prestige, which in turn could give further bonuses. We can modify the Main passage like this:

<<summary>>

<<if banditFail() >>
GAME OVER
<<else>>
[[Manage Knights]]
[[Next Turn|Main][nextTurn()]]
<<endif>>


Now we can modify the summary macro so the player will get a report on the bandit situation, and if banditFail is true, that is game over. Clearly we need to modify the scripts. I am going to break that up somewhat to explain the changes. First we need a variable to track the situation, banditRisk, and that must be initialised. We also need some phrases to inform the player.

var turn, knights, peasants, gold, banditRisk;

window.init = function() {
  turn = 0;
  knights = 0;
  gold = 100;
  peasants = 50;
  banditRisk = 50;
}

var banditRisks = [
  "-",
  "Some concern among the peasants about bandits in the hills",
  "Some peasants are claiming there are bandits in the hills",
  "Bandits have been seen in the area",
  "The bandits are becoming bolder",
  "The bandits have killed a couple of peasants; your people are demanding action",
  "The bandits have killed a large number of peasants, and are threatening the castle",
  "The bandits have overrun the castle",
];


The nextTurn function needs to be modified to update banditRisk. The more peasants you have, the more income and so the greater the risk of bandits. Having knights makes it go down. The value of banditRisk can never be below zero.

window.nextTurn = function() {
  turn++;
  gold += (state.active.variables.peasants * 2);
  gold -= (state.active.variables.knights * 20);
  // Knights quit if there is no gold for them
  if (gold < 0) {
    knights += (gold / 20);
    if (knights < 0) {
      knights = 0;
    }
  }

  // handle bandits
  banditRisk += (peasants * 2);
  banditRisk -= (knights * 5);
  if (banditRisk < 0) banditRisk = 0;
}


The next function returns true when banditRisk is high. How high depends on the number of risk phrases available. Note that I use banditRisks.length; we can add more phrases to the list without having to change anything here.

window.banditFail = function() {
  return (Math.round(banditRisk / 60) >= (banditRisks.length - 1));
}


Then I need to update the summary macro. This just adds one of the phrases defined earlier to the text, depending on how high banditRisk is. Just in the case the risk is sky-high, the last phrase is used whenever banditRisk is high, and we can use the banditFail function to test that.

macros.summary = {
  handler: function(place, macroName, params, parser) {
    var s = '';
    s += "Turn: " + turn;
    s += "\nKnights: " + knights;
    s += "\nPeasants: " + peasants;
    s += "\nGold: " + gold;
    new Wikifier(place, s);
    if (banditFail()) {
      new Wikifier(place, banditRisks[banditRisks.length - 1]);
    } else {
      new Wikifier(place, banditRisks[Math.round(state.active.variables.banditRisk / 60)]);
    }
  },
};




What next?

From here on you can make the game as complicated as you like. You probably need at least three variables that the player has to juggle and can only indirectly control. So far we have gold and banditRisk, but happiness would be a good addition. We can add more demographics to add more options, allow the player to set tax rates, etc..

Using JavaScript

Some general advice when coding in Twine:

1 Test the game often. At least do Build - Test play, even if you do not play through, as this will quickly check the JavaScript code can be understood. If you write three lines of code and Twine throws an error, you only have thee lines to check through. If you have written 20 lines since the last Build - Test play, you have a lot more lines to search through.

2 If there is a problem, scatter alert instructions though the code, each giving a different message. Play the game, and see what messages are displayed when to track down where the code is faulty.

Monday, 17 March 2014

RegExp in Twine

A RegExp is a regular expression (also called a RegEx), a special kind of string for matching against other strings.

Match

 A simple example of their use in a game might be to ask the player to type in an answer to a question, and to test if the answer is correct. you might try this:

if ($input == 'dragon') 

What if the player types "DRAGON" or "dragons" of " dragon"? None of these will match, as they are not exactly the same. Using a RegExp allows as to match the input against a pattern:

if ($input.match(/dragon/i)) 

Here we use the match method to compare the string against a pattern. the pattern itself has slashes at each, and a modifier, i, after the second slash. The i indicates that the pattern matching will be case insensitive, so this will match "Dragon". As long as the sequence "dragon" appears in the input, a match will be found.

Replace

The string class also support the replace method, which you can use to replace one piece of text with another. This also supports RegExps. Here is an example that will replace "story" with "tale":

s.replace(/story/, 'tale')

However, it will only replace the first occurance; it is usually better to add a modifier, g, to make is global. in this version, every occurance of story will be replaced.

s.replace(/story/g, 'tale')

Wild cards

The power of RegExp is that you can use wildcards to match against a string that is somewhat like what you expect. In this example, the \w will match any alphanumeric character (including underscore!). The thus sign indicates there can be any number of them, but at least one (use * to match against none or more).

/the \w+ story/
  the great story
  the 1st story

You can also match against alternatives:

/stor(y|ies)/
  story
  stories

Capture groups

You can also grab some text from the pattern and use it. You surround the text you want to capture in brackets in the RegExp, then use it in the replacement as a dollar followed by a number.

s.replace(//the (\w+) story//g, 'the $1 tale')
  the great story -> the great tale

Start and End

Often you will want to match against the start of the text and the end. The start can be matched against \A or ^, the end against \Z or $. This example will match only if this is the entire text - no more and no less.

/^The great story$/

Finally

RegExp is a big topi and this is really only scratching the surface, but is hopefully enough to give a feel for what it is. A couple of links with lists of codes you can use:

http://www.cheatography.com/davechild/cheat-sheets/regular-expressions/
https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions

Monday, 10 March 2014

The click macro (Sugarcube only)

If you are using Sugarcube, you get a whole new set of macros to play with.This a way of updating a page the player is on. In this example, the player finds a sword, and if she clicks on the link, the sword is picked up.

You are in a room, with <<id "link">><<click "a sword">><<set $weapon = 'fire'>><<append  "#fire">>You pick up the sword, and brandish it fiercely.<</append>><<replace "#link">>a sword<</replace>><</click>><</id>> on the floor.

<<id "fire">><</id>>


There is a lot going on there!

The <<click>> macro is the key. It takes a single argument with is the link text. When the player clicks the link, the macros inside <<click>> will be run.

The <<set>> macro sets a variable, so we can later test if  the sword was taken.

The <<append>> macro adds some text to he end of the HTML element named, in this case the span element with an id "fire".

The <<replace>> is similar, but removes the existing text (we need to do that to stop the player picking up the sword multiple times).

The <<id>> macro simply sets up a span with the given ID.


 Here is a something similar, but this time clicking the link displays a box with text; you can click the box to dismiss it. This can be used to allow the player to examine objects in the current location without sending her to another page.

You are in a room, with <<click "a button">><<set document.getElementById("hidden1").style.display="block">><</click>> on the floor.

<<id "hidden1">><<click "The button is big and red">><<set document.getElementById("hidden1").style.display="none">><</click>><</id>>


The stylesheet entry might be like this (this allows up to 5 links, named hidden1, hidden2...):


/* This is the panel that will appear */
#hidden1, #hidden2, #hidden3, #hidden4, #hidden5 {
  display:none;
  position:fixed;
  top:300px;
  left:200px;
  background-color:#eef;
  padding:20px;
  border-radius: 15px;
  border-color: #004;
}
/* This is the text in the panel  */
#hidden1 a, #hidden2 a, #hidden3 a, #hidden4 a, #hidden5 a {
  font-style:italic;
  color:#004;
}

/* links in the main passage */
.link-click {
  color:red;
}


Again the <<click>> macro adds the functionality to the text. In this case, the <<set>> macro is used to change whether the descruiption is displayed, by changing the "display" CSS property.

Saturday, 8 March 2014

Handling an inventory

Here is a set of macros that will make an inventory easy. Just dump this into a script passage, and be sure to call <<initInv>> right at the start.

window.getInv = function() {
  return state.history[0].variables.inventory;
}

macros.initInv = {
  handler: function(place, macroName, params, parser) {
    state.history[0].variables.inventory = [];
  },
};

macros.addToInv = {
  handler: function(place, macroName, params, parser) {
    if (params.length == 0) {
      throwError(place, "<<" + macroName + ">>: no parameters given");
      return;
    }
    if (state.history[0].variables.inventory.indexOf(params[0]) == -1) {
      state.history[0].variables.inventory.push(params[0]);
    }
  },
};

macros.removeFromInv = {
  handler: function(place, macroName, params, parser) {
    if (params.length == 0) {
      throwError(place, "<<" + macroName + ">>: no parameters given");
      return;
    }
    var index = state.history[0].variables.inventory.indexOf(params[0]);
    if (index != -1) {
      state.history[0].variables.inventory.splice(index, 1);
    }
  },
};

macros.inv = {
  handler: function(place, macroName, params, parser) {
    if (state.history[0].variables.inventory.length == 0) {
      new Wikifier(place, 'nothing');
    } else {
      new Wikifier(place, state.history[0].variables.inventory.join(', '));
    }
  },
};

macros.emptyInv = {
  handler: function(place, macroName, params, parser) {
    state.history[0].variables.inventory = []
  },
};


Here is a single page that shows it in action.

First we set up the inventory.<<initInv>>

Then we can use it:
You have <<inv>>.
You pick up a rake.<<addToInv rake>>
You have <<inv>>.
You pick up a spoon.<<addToInv spoon>>
<<if isInInv('spoon')>>You have a spoon<<else>>You don't have a spoon<<endif>>
You have <<inv>>.
You drop the rake.<<removeFromInv rake>>
You have <<inv>>.
You drop the rake again.<<removeFromInv rake>>
You have <<inv>>.
You pick up the spoon again.<<addToInv spoon>>
You have <<inv>>.
You drop nothing (error).<<removeFromInv>>
You have <<inv>>.
You pick up a hat.<<addToInv hat>>
You have <<inv>>.
You drop everything.<<emptyInv>>
<<if isInInv('spoon')>>You have a spoon<<else>>You don't have a spoon<<endif>>
You have <<inv>>.


Note that you can only add and remove one item from the inventory at a time, and you can only have one of anything in the inventory at a time. Also note that this stores strings not objects (if you do not know what that means, it probably does not matter).

Use the getInv function to get a list of objects in the inventory, and isInInv to test if an object is in the inventory.

If you use Sugarcube, you will need to modify the code so that every state.history[0].variables becomes state.active.variables.

Friday, 7 March 2014

Prerender and postrender in Twine

Twine supports things called  prerender and postrender. I was hoping to use postrender to blank lines between paragraphs. It will not do that (still not found a way for that), but I learn a few things a long the way.

This may well depend on which format you are using; this is based on Sugarcane.
Sugarcane has a content variable that is set like this when a page is being displayed:

var content = insertElement(passage, "div", null, "body content");

I guess content is an XML node.

It then goes through all the prerenders defined, calling each one in turn, with the content variable. I do not know what that order is, but I guess it is the order they are defined in, though what that means in Twine is not clear.

What prerender does is add nodes to content. To me, prerendering would be some kind of manipulation; what this is doing is prepending. This is a way to add a title to the top of every page, not a way to modify the text.

Next, the passage text is added to content:

new Wikifier(content, this.processText());

I would guess that this.processText() gets the text for the current passage and does some kind of processing. This is then "wikified", which presumably converts the Wiki conventions (eg //) to HTML (eg <i>), etc. This all gets added as new nodes for content.

Then any postrenders are called. Again, this is really just appending nodes to content, not postrendering in its normal sense.



Postrender and prerender are just functions that are defined as normal, something like this (in passages tagged as "script"):

prerender.myname = function(content) {
  new Wikifier(content, "This is my title\n");
}

postrender.myname = function(content) {
  new Wikifier(content, "This is my footer");
}

Note that the functions have to be attached to the postrender and prerender objects. This ensures Twee can find them.

You can call them anything you like, I have chosen "myname"; you can also call content anything you like, as long as you are consistent within a function. Note that the text in the prerenderer ends "\n", this ensures the title goes on its own line. This is not necessary for the postrenderer. You can put all the normal formating codes into the text, and use JavaScript to build up complex strings to be added.


Here is a neat postrenderer that adds footers to pages. A page tagged as "footer" contains the text to be added. Any page that shares a tag with a footer passage gets that footer added.
http://twinery.org/forum/index.php?topic=672.0

postrender.tagFooter = function(content) {
    var i,j, feet = tale.lookup("tags", "footer");
    for (i = 0; i < feet.length; i++) {
        for (j=0;j<this.tags.length;j++) {
            if (!feet[i].tags.indexOf(this.tags[j])) {
                new Wikifier(content, feet[i].processText());
            }
        }
    }
}

Addendum

It turns out that you can use postrender to manipulate the content of the page, as I found from this forum thread. This only seems to work in Sugarcube by the way.


function processTextNodes(node) {
  if (node.nodeType == 3) { // text node
    node.nodeValue = node.nodeValue
      .replace(/story/g, 'tale');
  } else {
    for (var i in node.childNodes) {
      processTextNodes(node.childNodes[i]);
    }
  }
}

postrender.subst = function(content) {
  processTextNodes(content);
}


All the postrender function, subst, does is call another function processTextNodes. This is a recursive function that works through the XML structure. The bit that does the work, and that you will want to modify, is in blue. In this case, all it does is swap the "story" for "tale". The important value you will be using is node.nodeValue which is just a string contenting a single paragraph of your passage.



I still could not use this to add extra lines; searching for both /\n/ and /<br>/ failed to find any matches, and this is when I realised that each line (in the JavaScript sense; each paragraph in Twine) is stored as a node. Adding to the end of a line is easy enough:

node.nodeValue = node.nodeValue + "<br>";

Unfortunately, this just shows  "<br>" at the end of a line, rather than giving an extra line. Adding "\n" had no effect.




Thursday, 6 March 2014

How to write macros

I wrote this as part of a learn exercise about macros. I am no expert; if you spot errors please do tell me and I will correct them. You should have some understanding of objects in JavaScript before reading this!

Introduction

Macros are used in passages like this:

<<macroname param1 param2 param3 ... >>

When Twine/Twee hits a section of text like this, it takes a look at the macros object. This object has a whole bunch of properties, and Twine/Twee tries to match macroname to one of those properties. if successful, it then calls the handler function. That may sound incredible obscure, but if we have a go at making our own macro, it may become more clear.

This is going to go in a script passage, because it is done in JavaScript. Here is our first macro, called first:

  macros.first = {
    handler: function(place, macroName, params, parser) {
    },
  };


It does not actually do anything if you have <<first>> in the passage, but it does exist in Twee as a macro.

It is just an object

Let us look at that in detail. If you read the [url=http://twinery.org/forum/index.php/topic,1516.0.html]Objects are Your Friends[/url] thread at Twinery.org, you will have seen this in the OP (I have removed the <<set>> for clarity):

$foo= {
  property1: "bar",
  property2: 894,
  property3: $baz,
}


It is doing the same thing! It is creating an object, and setting properties on the object. In one case the object is $foo, in the other it is macros.first. The $%foo object has three properties, called property1, property2 and property3; while macros.first just has one, called handler.

Why macros.first? Well, that attaches our new object first to the existing macros object. That means that Twee will find it when it looks for a macro.

Why handler? Because that is what Twee will try to use when it runs your macro.

The properties assigned to $foo are simple values; for a macro, you need to assign a function, and not just any function. Twee is going to call your function with four parameters. You can call those functions anything you like, but it is easier to stick with those names so you know what Twee will be sending.

Alternative format

You can also define your macro like this:

  macros['second!'] = {
    handler: function(place, macroName, params, parser) {
    },
  };


I do not think that that is as neat, but it does allow you more flexibility in what you name your macro - you can include various punctuation marks in the name. Personally, I would stick to only using letters, numbers and underscores, and use the first form.

Doing stuff

Here is a third macro, and this one actually does stuff.

  macros.third = {
    handler: function(place, macroName, params, parser) {
      new Wikifier(place, " macroName=" + macroName + " params=" + params + "");
    },
  };


This lets us see what some of those parameters are. The first is a representation of your passage (actually a HTMLDivElement object), and you can use that to add content to the page. Do that through the Wikifier. In brief, what that does is take the existing text (place) and append your new text. The basic use is like this:

  new Wikifier(place, mystring);
 
And you can change mystring however you like. You can have several lines like that in one macro, each one adding its own bit to the page.

In the example above, I am adding macroName and params. As you might guess, macroName is just the name of the macro. Params is an array of what comes after the name. For example:

<<third>>
<<third one two three>>


In the first instance, params would be an array of length zero, as there is no further text after the name. In the second instance, the array is:

['one', 'two', 'three']

Here is what is actually output:

macroName=third params=
macroName=third params=one,two,three


Macros in macros

You can use a macro inside a macro. The Wikifier will sort out running the inner macro. In this example, macro third is put in the text.

  macros.fourth = {
    handler: function(place, macroName, params, parser) {
      new Wikifier(place, "Using third: <<third " + params[0] + ">>");
    },
  };


Now we are assuming that there is a parameter. Never wise! Let us modify that macro to check that the parameter exists.

  macros.fourth = {
    handler: function(place, macroName, params, parser) {
      if (params.length === 0) {

        throwError(place, "<<" + macroName + ">>: no parameter given");
        return;
      }
      new Wikifier(place, "Using third: <<third " + params[0] + ">>");
    },
  };


You will see an extra line of code. First it checks if the number of parameters is zero. If it is, take evasive action! It throws an error with the given message, which immediately stops the rest of the macro doing anything.

Accessing variables

You cannot access variables in the normal way, but there is a way to do it. All variables you use in your passages are actually just properties of an object, state.history[0].variables (or
state.active.variables in Sugarcube). This macro puts the value of the variable $myname into the text.

  macros.fifth = {
    handler: function(place, macroName, params, parser) {
      new Wikifier(place, state.active.variables.myname);
    },
  };


The passage might look like this:

<<set $myname = 'Bob'>>
<<fifth>>


Wednesday, 5 March 2014

Initialising Variables

In a passage


Setting variables in a passage is easy. It is probably a good idea to have a passage dedicated to that purpose, and it might look like this:

<<nobr>>
<<set //initalising

$name = 'Boris';
$age = 43;
>>
<<endnobr>>

Note that all the work is being done inside a <<set>> macro. You can pack as much JavaScript in there as you like, and you do not need to actually set anything. What you do need is for it to start with two angle brackets, the word "set", a single space, some non-whitespace character. This will fail:

<<set
$name = 'Boris';

By the way, the double slashes, //, indicate tht what follows on that line is a comment.


In a script


Personally, I would prefer to keep my code and my text separate as far as possible. All that JavaScript should go into a script passage. Sounds easy; you might think this would work:

$name = 'Boris';
$age = 43;

It does not (probably due to scoping rules).

In Sugarcube, you have to do this:

state.active.variables.name = 'Boris';
state.active.variables.age = 43;


In Sugarcane (and I guess the others):

state.history[0].variables.name = 'Boris';
state.history[0].variables.age = 43;


There is an added complication in Sugarcane that the state object  does not exist when a script passage is first run (in Sugarcube it does, so this does not apply). What you have to do then is to set the variable inside a function, and call the function. Your script will look like this;

window.myinit = function() {
  state.history[0].variables.name = 'Boris';
  state.history[0].variables.age = 43;
}

And you invoke it on the Start page like this:

<<set myinit() >>

Tuesday, 4 March 2014

Twine Coding

I am going to use this page to post interesting and useful code snippets for Twine. These are all tested in Sugarcane, by the way. There is some difference between the formats, in particular Sugarcube is different.

If Twine cannot find a macro with a given name, it will instead look for a passage and insert that (not with sugarcube, though). This is an easy way to include variables. One passage could then include this text:

<<playername>> attacks the gruesome fiend with <<playerweapon>>.

You can then set up two passages, playername and playerweapon, that will automatically get inserted. Note that these passage names cannot contain spaces (and obviously cannot be the same as an existing macro).

The playername passage might look like this:

<<print $name>> the <<print $rank>>

The playerweapon like this:

<<if $weapon = 1 >>
Sword of Fire
<<elseif $weapon = 2 >>
Spear of Might
<<elseif >>
his fists
<<endif>>


In Sugarcube, you have to do this (it does not support the display shortcut):

<<display "playername">> attacks the gruesome fiend with <<display "playerweapon">>.



You can define functions in a script passage, but you have to attach the function to the window object to make it accessible. Here is how to do that:

window.myFunction = function() {
  alert("Here I am");
};


I am still working on how to access variables and objects defned in a script passage



The <<set>> macro is effectively an eval function; if just runs the text though a JavaScript parser. This means you can call function, concatenate commands, etc.

<<set alert("Here I am"); count += 1 >>

Even:

<<set
n = $fullname.indexOf(' ');
if (n == -1) {
  $name = $fullname;
} else {
  $name = $fullname.slice(0, n);
}
>>


Monday, 3 March 2014

The "action" Macro

The <<action>> macro is useful for when you want to present the player a set of choices several times, but at each point, previously chosen options cannot be chosen again. For example, the player arrives at a junction, and can go any of three ways. Ahead leads to the rest of the game, but he might first go left, then come back here, then have the option to go ahead or right.

Set up the options in a passage of their own. It might look like this (in this case, I called the passage "Courtyard actions"):

<<actions
"Talk to the men"
"Try the big double doors"
"Try the small door"
"Leave the castle" >>


In the passage where the player arrives at the courtyard looks like this:

The stairs led down to another door, which opened into a courtyard. Two men were repairing a cart.

<<display "Courtyard actions">>


All this does is import the "Courtyard actions" into the text, so the player sees four options. The "Try the big double doors" is similar:

The hero went to the big double doors, and tried to open them, but they were locked.

<<display "Courtyard actions">>


But in this case any choices that have already been taken will not get displayed. Talking to the men has other choices:

The hero walked over to the men repairing the cart. 'Hi.'
They looked her up and down. 'Hi!'

[[Ask them to take her out of the castle]]
[[Try to learn more]]
<<display "Courtyard actions">>

Sunday, 2 March 2014

Handling variables that have not been initialised

What happens if you forget to initialise a variable that you are using to track state? If you are using Sugarcube, it is going to generate an error when the player gets to a passage that uses it, right?

Not if you test to see if it is defined first.

Here is an example. The variable $weapon is used to hold the player's current weapon (as an integer).

You confront the goblin with
<<if $weapon === undefined>>
your bare hands
<<elseif $weapon = 1 >>
the Sword of Fire
<<elseif $weapon = 2 >>
the Spear of Might
<<elseif >>
yours fists
<<endif>>
and it flees in terror.


The first if tests whether the variable is undefined, and if not acts accordingly. If it is defined, the rest of the code handles it based on its value.

This is great for use in a display passage.

You confront the goblin with <<display "playerweapon">> and it flees in terror.

::playerweapon
<<if $weapon === undefined>>
your bare hands
<<elseif $weapon = 1 >>
the Sword of Fire
<<elseif $weapon = 2 >>
the Spear of Might
<<elseif >>
yours fists
<<endif>>


In other formats (Sugarcane, etc.) variables default to zero, so this is not relevant.

Saturday, 1 March 2014

Using visited and visitedTag

These two functions, visited and visitedTag, are very useful for maintaining state with a minimum of effort. Twine remembers every page the player has visited, and so you can check where she has been previously. Let us suppose there was an option to go to a page "Get the big sword", and some time later, the player is encountering a monster. We might do this:


<<if visited("Get the big sword") >>
You stand your ground, branishing the big sword you found earlier. Intimidated, the goblin runs away, allowing you to [[explore more]]
<<else>>
The goblin leaps at you, tearing you apart with his dirty claws. As you llie dying, you wonder if there was any way you could have found a weapon.

<<endif>>

Note that the link appears inside the conditional text.

It is important to type this accurately. This will fail:

<<if visited("get the big sword") >>

You need to get the page name exactly right. Also, there must be no spaces before "if", or either side of "else" and "endif".

The visitedTag function is similar, but checks if the player has visited pages with the given tag. This is useful if the game should check if the player has visited any one of a set of pages. Perhaps there are three places the player could get that big sword, and you want to check if she hasd been to any of them. there might be an number of passages in which the player gets injured, and you want to check if she has been to them. here is an example (note that tags cannot have spaces):

<<if visitedTag("bigsword") >>

Unfortunately, Sugarcube does not support visitedTag.

ETA: The day after I say  Sugarcube does not support visitedTag, it gets added to Sugarcube. Hurray!

Friday, 28 February 2014

Text Style

Twine uses HTML so styling is done with CSS.

To change the style of the whole game, create a passage and tag it "stylesheet"; put the CSS in there (the best way to do that is to Google stylesheets other people have created for your Twine format (e.g., Sugarcane), and paste that in, and then to modify it to suit your story.

If you have a set of passages that need an alternative style, create a second passage tagged as "stylesheet", but give it another tag too. Any pages with that new tag will automatically be styled according to the second stylesheet, rather than the first.

If you have sections with passages that need an alternative style, the best way seems to be to wrap them in a <div> and <span>, with a class defined in your stylesheet.

Here is a link to some example stylesheets:
http://www.glorioustrainwrecks.com/node/5163

Wednesday, 26 February 2014

Planning a Twine Game

This is how I approach designing a CYOA game for Twine:

Genre and Setting

Decide where it is set.

What is happening

A simple overview of what will happen. If the player is reacting to events in the world, then what are those events? If she is to foil the plot of the evil supergenius, exactly what is that evil supergenius trying to do.

On the other hand, if the world is carrying on as normal, a simple one sentence description might be enough.

Outcomes

What are the meaningful outcomes? Aim for about half a dozen, and try to have some good aspect to all of them, even if there is a downside too. You died, but you thwarted the evil genius. Or each outcome could be a success for a different faction, and the player has influenced which one.

You might choose to write these out as they will appear in game at this stage.

Choke points

Identify pages that the player will always go through (or usually go through or...). Of course, this will depend on your game, maybe there are broadly two ways through, each with their own set of choke points, the player does one or the other.

Again, you may choose to write these out as they will appear in game at this stage.

A beginning

Write that first page!

Join the dots

Now all you have to do is create the pages linking the beginning to the chokepoints to the outcomes. Simples.

Tuesday, 25 February 2014

Designing a game for Twine


I have been thinking about CYOA games recently, and thought I would share my ideas. Obviously, this is just my opinion.

Traditional IF tends to revolve around puzzles, and I am not sure how well that can be done in a CYOA, where it is relatively easy to try every possibility and see what works. I think that what a CYOA does best is to allow a story to evolve in a way that the player can influence the outcome. That means you need a variety of meaningful outcomes (the character dying is not a meaningful outcome, no matter how bizarre it happened).

On the subject of meaningful, you also need the choices presented to the player to be meaningful. If it is clear that A is the right choice and B the wrong choice, why bother giving the option of B? If choosing A and B both take the player to the same place, why bother with the choice? Okay, sometimes choices will be like this, but you should aim to minimise them.

On the other hand, you do not want to have each thread leading to its own meaningful outcome. If the player goes through six pages, with two choices on each page, you need 32 outcomes (assuming no recombination or pruning). Good luck making all of them meaningful!

The solution is to have threads converge at chokepoints. To make the choices before a chokepoint meaningful, your game needs to remember which pages the player has visited and to be able to react to that later on.

Monday, 24 February 2014

Beginnings

Recently I had a go at using Twine. If is a way to create choose you own adventurers (CYOAs), and it is incredibly easy to use. Right click somewhere to create a new page (or passage as it calls them), then double click to edit it. give it a name, and type some text. To link to another page, put the text in double square brackets.

You can go to [[another page]].

Just make sure the other page is called "another page", and that is it. The link in the first passage will be blue if the connect is good, and you will see an arrow from one page to another. Pages with no links to anywhere are a different colour, and pages to broken links are flagged with a warning system (though it does not update immediately; click on a passage to select it, and it will then check if the links are all okay).

I actually prefer to have named passages, so do links like this:

You can go to [[another page|Second Page]].

The player will see "You can go to another page.", but the other page is called "Second Page".

So why am I struggling with Twine? I am not (except for getting on the official forum; that appears to be impossible). But I have other blogs on subjects I am or were struggling with, and this maintains the theme.