Quantcast
Channel: Digital Ephemera » Twine Tuesday
Viewing all 43 articles
Browse latest View live

Twine Tuesday: Dynamically Adding Passages

$
0
0

[At the beginning of this month, I started writing a series of tutorials covering different tools for creating interactive fiction. The first of these was on Twine and, through three different parts, I wrote nearly 30 pages (of mostly pictures) beginning with the Start passage up through writing macros to go along with the four videos I made too. However, I haven't written anything here about Twine.

That is going to change right now as I've decided to start posting different macros, functions, and CSS tricks I've picked up along the way -- and even some I've created myself. I'm not sure if I will be able to maintain a schedule of a new post every week, but all these will be part of the category 'Twine Tuesday'.]

Normally, for passages to exist in Twine, they have to be created within the editor and produced as part of a HTML file. Some branching can happen between passages, but it is driven by variables and conditional statements. Everything has to be made beforehand.

Through the Twee API, it is possible to access the Tale object (which holds the passages) and use its functions to get() and loopup() different passages. However, while this allows a developer to change content in existing passages, there isn’t a method to add passages to the Tale object after the HTML file is produced. That is what this code does.

By using the prototype property of all JavaScript objects, the Tale object can be augmented to have the addPassage() function. Through adding an element to the page and then to the passages array within the Tale object itself, a new passage can be added to the story dynamically. However, this does not change the content of the current passage.

To do that, a macro is needed. By calling “addLink” with the parameters of the new passage’s title and its content, the addPassage() function can be called on the current instance of the Tale object and the new passage can be added to the page. By making a new Wikifier object, a link can be added in place of the macro in the passage which invoked it. Combining the two adds a passage to the overall story and a new link to that passage with the use of the macro.

Note: This does not work with the Sugarcane StoryFormat. It also bypasses the History object, so rewinding and restarting may not work as expected.

Source:

:: Start
<<addLink "A new link" "All new content. Pretty cool, right?" >>

:: script [script]
Tale.prototype.addPassage = function(title,tags,content) {
	var newPassage = document.createElement("div");
	newPassage.id = title;
	newPassage.setAttribute("tiddler",title);
	newPassage.setAttribute("tags",tags);
	newPassage.setAttribute("modifer","twee");
	newPassage.innerHTML = content;
	$("storeArea").appendChild(newPassage);

	this.passages[title] = new Passage(title,$(title));

};

macros['addLink'] = {
	handler: function(place, object, parameters)	{
      tale.addPassage(parameters[0],"",parameters[1]);
   		new Wikifier(place, "[["+parameters[0]+"]]");
	}
}

Filed under: Twine Tuesday Tagged: dynamic passages, twine

Twine Tuesday: Endless Story

$
0
0

When I covered my code to dynamically create passages, I went for the fast solution. In trying to create a way to add passages at run-time, I showed one way to do it, not necessarily the best method. That’s what I found out when I used it for last week’s story Mixed Signals.

What needs to change is, instead of using innerHTML, to switch to innerText/textContent. The reason for this is because, when a browser edits the DOM using innerHTML, it parses the string content as tags. It assumes that you are passing in valid HTML content, not just text.

Tale.prototype.addPassage = function(title,tags,content) {
	var newPassage = document.createElement("div");
	newPassage.id = title;
	newPassage.setAttribute("tiddler",title);
	newPassage.setAttribute("tags",tags);
	newPassage.setAttribute("modifer","twee");

	if(newPassage.innerText) {newPassage.innerText = content;}
	else {newPassage.textContent = content;}

	$("storeArea").appendChild(newPassage);
	this.passages[title] = new Passage(title,$(title));
};

Now, if you want to pass in TiddyWiki code, you can.

tale.addPassage(passageID,"<<addLink 'Make another' '' >>");

For example, the complete code at the bottom of this post demonstrates an endless story which create elements, adds them to the page, and then makes a new index in the Passages array. (Works only in the Jonah Story Format currently.)

The key is in creating a new passage title. They have to be unique per passage, so a random number is added to the end before being placed in the array. However, because it is a random number, the title isn’t as important visually now, so I’ve also included the CSS to remove it from being rendered by the browser.

Source:

:: Start
<<addLink "Make a passage" "" >>

:: script [script]
Tale.prototype.addPassage = function(title,tags,content) {
	var newPassage = document.createElement("div");
	newPassage.id = title;
	newPassage.setAttribute("tiddler",title);
	newPassage.setAttribute("tags",tags);
	newPassage.setAttribute("modifer","twee");

	if(newPassage.innerText) {newPassage.innerText = content;}
	else {newPassage.textContent = content;}

	$("storeArea").appendChild(newPassage);
	this.passages[title] = new Passage(title,$(title));
};

macros['addLink'] = 
{
	handler: function(place, object, parameters)
	{
  var newID = Math.floor(Math.random()*65456);
		tale.addPassage("n"+newID,"","<<addLink 'Make another' '' >>");
		new Wikifier(place, "[["+parameters[0]+"|" + "n" + newID + "]]");
	}
}

:: styles [stylesheet]
.passage .title { display: none }

Filed under: Twine Tuesday Tagged: Twine Tuesday

Twine Tuesday: Loading External JavaScript

$
0
0

Since I come from a background in web development, I have been finding myself frustrated with Twine more often than not. I keep wanting to use more complex code in my stories and the Twine editor, the current version anyway, doesn’t always make it simple to do that. I often have to turn to complex solutions of mixing functions, macros, and overly complicated loading methods to make things work the way I want.

I’ve also noticed several people have been bypassing all this work by turning to including jQuery in their code. And I’m not going to argue against using it. It’s a great solution to this issue. However, I personally have problems with it in reference to the editor itself. Previously, in order to use libraries like that, you had to edit the file, after working in Twine, to add it to the HTML head manually.

Yes, I did write “previously” there. I have a working solution around this too. (It’s what I used for last week’s Voigt-Kampff project.)

macros['loadJS'] =
{
	handler: function(place, object, parameters)
	{
		var se = document.createElement("script");
		se.type = 'text/javascript';
		se.src = parameters[0];
		var hT = document.getElementsByTagName("HEAD")[0];
		hT.appendChild(se);
		if(se.innerText) {eval(se.innerText);}
		else {eval(se.textContent);}
	}
}

This code will fetch, add, and load an external JavaScript file. However, and I often see very few people adding this when they post similar code, this is a potential security risk. Make sure you trust any code before you add it to your story. There is no checking with this. It will have the browser run the code directly.

The example for this week’s macro requires two separate files.

alertCode.js

alert("You ended the world!\n\nWhy did you do that? Why?\n\n" +
"Did you not like it? Was that why?\n\n" +
"What? The button? Oh. Okay then. Yeah. That's fine.\nCarry on.");

Story Source:

:: Start
You sit before a computer console full of buttons. There are large, small, square, and triangle ones. All different sizes and shapes.

As you scan across them though, one draws your eye. It is big, red, and looks important. You feel you //must// press it.

[[Big Button of Doom]]

:: script [script]
macros['loadJS'] =
{
	handler: function(place, object, parameters)
	{
		var se = document.createElement("script");
		se.type = 'text/javascript';
		se.src = parameters[0];
		var hT = document.getElementsByTagName("HEAD")[0];
		hT.appendChild(se);
		if(se.innerText) {eval(se.innerText);}
		else {eval(se.textContent);}
	}
}

:: Big Button of Doom
<<loadJS "alertCode.js">>

Should anyone think I am against using jQuery, here is the code to load that too from Google’s CDN.

macros['jQueryRemoteLoading'] =
{
	handler: function(place, object, parameters)
	{
		var se = document.createElement("script");
		se.type = 'text/javascript';
		se.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js";
		var hT = document.getElementsByTagName("HEAD")[0];
		hT.appendChild(se);
		if(se.innerText) {eval(se.innerText);}
		else {eval(se.textContent);}
	}
}

Filed under: Twine Tuesday Tagged: jQuery, twine

Twine Tuesday: No-click Twine (without jQuery)

$
0
0

Like many other people who follow the work done in the Twine scene, I was excited to see Christine Love’s Even Cowgirls Bleed. It was the first working project I’d seen that got away from the “click” needed to progress in Twine. Unlike many other people though, I was also eager to find out how it all worked.

See, she beat me to figuring out how to do “no click” in Twine.

I’d been working on a way to have the same effect (without jQuery) and wasn’t having much success. I’d get it to work in one browser, and then find out it now didn’t work in another. It was a constant struggle to balance all the bizarre different ways IE, Firefox, and Webkit browsers handle and register event listeners.

Finally though, here was a working solution I could use as a basis to build my own code. And, within a few hours of pouring through it, I saw what I was missing, integrated it into my stuff, and had a working solution myself. Then, of course, I learned Safari, but not Chrome, had trouble with it.

A few days and many hours of work later, I had something that worked nearly everywhere (it still doesn’t work in IE 8 or earlier) and was relatively straightforward. Still, this is a work in progress and should be treated as such. However, I’ve tested it in IE 9+, and the latest versions of Firefox, Chrome and Safari. It seems to work correctly across them.

Source:

:: Start
[[Test]]

:: script [script]
/*
 mouseEvent() from http://marcgrabanski.com/articles/simulating-mouse-click-events-in-javascript
*/
function mouseEvent(type, sx, sy, cx, cy) {
  var evt;
  var e = {
    bubbles: true, cancelable: (type != "mousemove"), view: window, detail: 0,
    screenX: sx, screenY: sy, clientX: cx, clientY: cy,
    ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
    button: 0, relatedTarget: undefined
  };

  if (typeof( document.createEvent ) == "function") {
    evt = document.createEvent("MouseEvents");
    evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
    e.screenX, e.screenY, e.clientX, e.clientY,
    e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
    e.button, document.body.parentNode);
  } else if (document.createEventObject) {
    evt = document.createEventObject();
    for (prop in e) {
      evt[prop] = e[prop];
    }
    evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
  }
  return evt;
}

function dispatchEvent (el, evt) {
  if (el.dispatchEvent) {
    el.dispatchEvent(evt);
  } else if (el.fireEvent) {
    el.fireEvent("on" + type, evt);
  }
  return evt;
}

var oldCreateInternalLink = Wikifier.createInternalLink;
Wikifier.createInternalLink = function(place, title) {

   link = oldCreateInternalLink(place, title);

   if (link.addEventListener) 
   {
       link.addEventListener('mouseover', function () {
            var evt = mouseEvent("click", 0,0,0,0);
            dispatchEvent(this, evt);
            this.removeAttribute("href");
            this.style.display = "none";
            this.removeEventListener("mouseover", function(){});
       }, false); 
   } 
   else if (link.attachEvent)  
   {
      link.attachEvent('mouseover', function () {
           var evt = mouseEvent("click", 0,0,0,0);
           dispatchEvent(this, evt);
           this.removeAttribute("href");
           this.style.display = "none";
           this.detachEvent("mouseover", function(){});
      }, false);
    }
    return link;
}

:: Test
[[Test 2]]

:: Test 2


Filed under: Twine Tuesday

Twine Tuesday: Timed Replacement of Content

$
0
0

For the few weeks I have been doing these posts, this is the first one in which I highlight code someone else has done. The reason for this change is a simple one: I’ve been using this particular macro for a few different Twine projects lately (notably Tree and R_Caved). It’s far past time for me to credit and, hopefully, spread this solution around for others to use.

Despite first seeing this listed on Glorious Trainwrecks, I didn’t know who “L” was for a few weeks. That is, not until Leon Arnott posted in the (unofficial) Twine community on Google+ and took credit for it. So, thanks for posting this code, Leon!

Source:

:: Start
<<timedreplace 4>> Initial text. <<replacewith>>This replaced the initial text. <<endtimedreplace>>

<<timedreplace 6>>This showed up after 6 seconds.<<endtimedreplace>>

:: script [script]
version.extensions['timedreplaceMacro'] = {major:1, minor:0, revision:0};
  macros['timedreplace'] = {
    handler: function (g, e, f, b) {
		function tagcontents(starttag, endtag, k) {
			var a = b.source.slice(k);
			var l = 0;
			var c = "";
		    for(var i = 0; i < a.length; i++) {
				var w = endtag.length;
				if(a.substr(i, w) == endtag) {
					if(l == 0) {
						b.nextMatch = k + i + w;
						return c;
					}
					else {
						l--;
						c += a.charAt(i);
					}
				}
				else {
					if(a.substr(i, starttag.length) == starttag) {
						l++;
					}
					c += a.charAt(i);
				}
			}
			return "";
		}
		var tr="<<timedreplace";
		var rw="<<replacewith>>";
		var k = b.source.indexOf('>>', b.matchStart) + 2;
		var c = tagcontents(tr, rw, k);
		var d = tagcontents((c ? rw : tr), "<<endtimedreplace>>", c ? b.nextMatch : k);
		var h = null;
        if(c) {
			h = insertElement(g, "span", null, "timedreplacement1");
			new Wikifier(h,c);
		}
		if(d) {
		    var m = insertElement(g, "span", null, "timedreplacement2", d);
			m.style.display = "none";
			setTimeout(function() {
				if (m) { 
				  if (h && h.parentNode) h.parentNode.removeChild(h);
				  var t = m.firstChild ? m.firstChild.nodeValue : "";
				  removeChildren(m);
				  new Wikifier(m,t);
				  m.style.display = "inline";
				  fade(m, { fade: "in" });
				}
			},(parseInt(f[0]) || 0)*500);
        }
        else {
            throwError(g, "can't find matching endtimedreplace");
			return;
        }
    }
  }
  macros['endtimedreplace']={handler: function () {} }
  macros['replacewith']={handler: function () {} }


Filed under: Twine Tuesday Tagged: Twine Tuesday

Twine Tuesday: Quest log

$
0
0

The other day, there was a post in the Twine community on G+ asking if anyone knew of a macro to keep track of tasks and then be able to mark them off. Since I was up early, which is something that has been happening more often lately, I thought I’d try to implement it myself. About 30 minutes later, I had posted my solution.

The major change between this and the one I originally posted is some additional CSS instead of setting style manually. It’s what I meant to add the first time, but left it up to other authors to change themselves.

:: Start
<<makeQuests>> 
<<addQuest "Feed Chickens">> 
<<removeQuest "Feed Chickens">> 
<<addQuest "Water the lawn">> 
<<removeQuest "Water the lawn">> 
<<addQuest "Milk the cow">> 
<<addQuest "Get the mail">>

:: script [script]
(function(){ 

if ( !Array.prototype.forEach ) { 
 Array.prototype.forEach = function(fn, scope) { 
 for(var i = 0, len = this.length; i < len; ++i) { 
 fn.call(scope, this[i], i, this); 
 } 
 } 
} 

var Quest_array = []; 

function addQuestDiv() 
{ 
 var el = document.createElement("div"); 
 el.id = "Quest"; 
 document.body.appendChild(el); 
} 
function updateQuest() 
{ 
 $("Quest").innerHTML = ""; 
 Quest_array.forEach(function(element,index,array) { 
 if(element[1] === true) { 
 $("Quest").innerHTML += "<p style='text-decoration:line-through; color: black;'>" + element[0] + "</p>"; 
 } 
 else { 
 $("Quest").innerHTML += "<p>" + element[0] + "</p>"; 
 } 
 }); 
} 

macros['makeQuests'] = {handler: function() { addQuestDiv(); } } 

macros['addQuest'] = { 
 handler: function (place, object, parameters) { 
 Quest_array.push([parameters[0], false]); 
 updateQuest(); 
 } 
} 
macros['removeQuest'] = { 
 handler: function (place, object, parameters) { 
 Quest_array.forEach(function(element,index,array) { 
 if(parameters[0] === element[0]) 
 element[1] = true; 
 }); 
 updateQuest(); 
 } 
} 
}());

:: style [stylesheet]

#Quest { 
 position: fixed;
 top: 60px; 
 right: 0px;
 background: #707070;
 width: 120px; 
 padding: 10px;
 font-size: 1.2em;
 color: #ff9900;
 border: 2px solid grey;
 -moz-border-radius: 8px;
 border-radius: 8px;
}

Filed under: Twine Tuesday

Twine Tuesday: Markup Syntax in 1.3.5

$
0
0

(This is going to be a short post today. Between getting home very late from work and then hanging out with a friend of mine I rarely see, I don’t have much time left to get this out before the day ends.)

I’ve been slowly learning the extra syntax that Twine supports from its underlining engine of TiddyWiki. Like, for example, enclosing some text with “//” will give it an Italics emphasis. Using “==” will mark it with a strike-through style.

However, unlike my learn-it-when-I-need-it approach, Leon Arnott has done a fantastic job of demonstrating nearly all possible variations of valid syntax on this page. There’s even some examples of combining in-line CSS with Twine’s own “<html>” tag to give authors an approach that doesn’t use separate stylesheet passages. I highly recommend checking out the examples as a way to further learn what Twine is capable of doing.


Filed under: Twine Tuesday Tagged: twine

Twine Tuesday: Dynamic Dialogue

$
0
0

[This is the last scheduled Twine Tuesday post for awhile. I might come back to doing these later this year, but I don't get the feeling that they are as helpful to others as they could be. It's mostly just me posting my own code.]

For those following my weekly Twine projects, you may have noticed that I have become really interested in algorithmically generated stories lately. It’s taking some core text and having code, often randomly, make decisions about its placement, size, or even content. It’s also a trend I’m going to follow for a few more weeks too.

However, I thought I would point out something that has become key to these projects: passages can be storage. You don’t have to display passages written in Twine. They can, in fact, be used to hold content that will be reference via macros or with JavaScript.

For example, consider the following short passage:

:: HamletLines
Well, God-a-mercy.
Excellent well; you are a fishmonger.

They are lines 172 and 174 from Hamlet, Act 2, Scene 2. Normally, the paradigm is to create a link to that passage at some point. However, that doesn’t necessarily have to happen. Using that passage as storage, it could be referenced by a call to Tale.get() to retrieve its content and then used for another purpose.

var Hamlet = tale.get("HamletLines");

In fact, by placing special delimiters within the text of a passage, it can be parsed by calling String.split() to chop up a text and create an array of its lines. Everything that is in-between whatever that delimiter and the next instance of it is will become an index in an array. In turn, using a randomly chosen number or simply incremental counter, lines can be picked out and placed in another passage. (This technique has been around for a long time in various text parsers, but it didn’t occur to me to use it with Twine until a couple weeks ago.)

:: HamletLines
Well, God-a-mercy.&nbsp; 
Excellent well; you are a fishmonger.&nbsp; 
Then I would you were so honest a man.&nbsp; 
Ay, sir; to be honest, as this world goes, is to be
one man picked out of ten thousand.&nbsp; 

:: PoloniusLines
Do you know me, my lord?&nbsp; 
Not I, my lord.&nbsp; 
Honest, my lord!&nbsp;
That's very true, my lord.&nbsp; 
I have, my lord.&nbsp;

...

macros['dialogue'] = 
{
   handler: function(place, object, parameters)
   {	
      var Hamlet = tale.get("HamletLines");
      var Polonius = tale.get("PoloniusLines");
      H_wordList = Hamlet.text.split("&nbsp;");
      P_wordList = Polonius.text.split("&nbsp;");
   }
}

Combining this with calling Tale.add(), as I have done in the full example, would allow for new passages to be added with the text from each of the characters. Using setInterval() then breaks up the dialogue over time.

Working Example

Source:

:: Start
<<dialogue>>

:: script [script]
(function()
{

var line = 0;
var H_wordList = [];
var P_wordList = [];
var text = "";

Tale.prototype.add = function(title,tags,content) {
   var pTitle = unescape(encodeURIComponent(title));
   var el = insertElement($("storeArea"),"div",pTitle,"",content);
   el.setAttribute("tiddler",pTitle);
   el.setAttribute("tags",tags);
   el.setAttribute("modifer","twee");
   var newP = new Passage(pTitle,$(pTitle));
   this.passages[title] = newP;
   if(tags === "stylesheet")
   { addStyle(newP.text); }
   else if(tags === "script") 
   {try {eval(newP.text);}
   catch(e) {alert("There is a technical problem with this story (" + newP.title + 
   ": " + e.message + 
   "). You may be able to continue reading, but all parts of the story may not work properly.");}
}
};

var dialogue = function() {
   if(line < 11)
   {
		text = "''Hamlet:'' " + H_wordList[line];
		tale.add("Hamlet"+line, "", text);
		state.display("Hamlet"+line, null);

		text = "''Polonius:'' " + P_wordList[line];
		tale.add("Polonius"+line, "", text);
		state.display("Polonius"+line, null);
		line++;
    }
};

macros['dialogue'] = 
{
	handler: function(place, object, parameters)
   {	
		var Hamlet = tale.get("HamletLines");
		var Polonius = tale.get("PoloniusLines");
		H_wordList = Hamlet.text.split("&nbsp;");
		P_wordList = Polonius.text.split("&nbsp;");
  timer = setInterval(dialogue, 7800);
	}
}
}());

:: HamletLines
Well, God-a-mercy.&nbsp;
Excellent well; you are a fishmonger.&nbsp;
Then I would you were so honest a man.&nbsp;
Ay, sir; to be honest, as this world goes, is to be
one man picked out of ten thousand.&nbsp;
For if the sun breed maggots in a dead dog, being a
god kissing carrion,--Have you a daughter?&nbsp;
Let her not walk i' the sun: conception is a
blessing: but not as your daughter may conceive.
Friend, look to 't.&nbsp;
Words, words, words.&nbsp;
Between who?&nbsp;
Slanders, sir: for the satirical rogue says here
that old men have grey beards, that their faces are
wrinkled, their eyes purging thick amber and
plum-tree gum and that they have a plentiful lack of
wit, together with most weak hams: all which, sir,
though I most powerfully and potently believe, yet
I hold it not honesty to have it thus set down, for
yourself, sir, should be old as I am, if like a crab
you could go backward.&nbsp;
Into my grave.&nbsp;
You cannot, sir, take from me any thing that I will
more willingly part withal: except my life, except
my life, except my life.&nbsp;

:: PoloniusLines
Do you know me, my lord?&nbsp;
Not I, my lord.&nbsp;
Honest, my lord!&nbsp;
That's very true, my lord.&nbsp;
I have, my lord.&nbsp;
How say you by that? Still harping on my
daughter: yet he knew me not at first; he said I
was a fishmonger: he is far gone, far gone: and
truly in my youth I suffered much extremity for
love; very near this. I'll speak to him again.
What do you read, my lord?&nbsp;
What is the matter, my lord?&nbsp;
I mean, the matter that you read, my lord.&nbsp;
Though this be madness, yet there is method
in 't. Will you walk out of the air, my lord?&nbsp;
Indeed, that is out o' the air.
How pregnant sometimes his replies are! a happiness
that often madness hits on, which reason and sanity
could not so prosperously be delivered of. I will
leave him, and suddenly contrive the means of
meeting between him and my daughter.--My honourable
lord, I will most humbly take my leave of you.&nbsp;
Fare you well, my lord.&nbsp;

:: style [stylesheet]
body {
	background: #333333;
}

.passage {
		font-size: 3.1em;
		background: transparent;
		color: white;
		width: inherit;
}

.passage .title { display: none } 

h1 {
 color: white;
 font-size: 3.3em;
}

#sidebar { display: none;}

#footer {display: none;}

#floater { display: none; }

:: StoryTitle
Hamlet: Act 2, Scene 2, Lines 172-218

Filed under: Twine Tuesday

Using Google Drive to host Twine (and other HTML) files

$
0
0

 

Update: As of August 7, 2014, this specific method no longer works. Once a new way is found, or Google changes its code again, I’ll update the guide to reflect that.

 

Since I’ve started doing various tutorials this year on using Twine, the most common question I get is how to host files. More than the details of doing command loops, creating macros, or even changing styles using CSS, most people just want to know how to share what they have created with more people to get feedback on their work.

On the surface, it seems like a simple question too. However, because it often involves uploading files to a remote server, it becomes more difficult from a technical and economic perspective. Most websites charge money to share files and, even when purchased, it might then require access through FTP or even SSH just to post a HTML file created in Twine.

That’s obviously way too many steps for most people and instead of doing all that, they turn to services like Dropbox or Box.com. These offer the ability to publicly share files for free and, for many, it is enough. However, there does exist another solution too. For those who already have a Google account from Gmail or YouTube, files can be hosted from within Google Drive.

1) From Google Drive, create a new folder.

createFolder

 

2) Name the folder

nameFolder

 

3) Open the Share options

shareFolder4) Under Who has access, click on “Change…”

changeAccess

 

5) Under Visibility options, change it to “Public on the web” and save.

visSettings

 

6) Open the folder created in Step 2

openFolder

 

7) Click on the upload button and select “Files…”

uploadFiles

 

8) Select the file.

selectFile

 

9) Confirm the sharing.

confirmShare

 

10) Click to open the file.

google-drive_twine2-111) Click on “Open” to edit the file.

google-drive_twine

 

12) Click on the “Preview” button.

previewButton

 

13) Copy the public link.

google-drive_twine3


Filed under: #TwineTuesday, howto, Twine Tuesday Tagged: twine

Twine: Share your macros, post your code, and write more tutorials — now with official wiki!

$
0
0

For me, it all started with Cyberqueen. That was the first Twine game I played, and it become the basis of what I thought was possible with a Twine game last year. It was even, for the initial couple weeks of contact with it, how I thought all Twine games worked too. There would be timed events, multiple colors, and a disturbingly delightful intoxicated chaotic textual world.

It had a major influence on me. Both as art, as a game, and mostly importantly for Porpentine‘s willingness to post the code behind it. Even though it was part of the normal Ludum Dare jam rules (to post the source too), I was able to learn from Porpentine’s work. I reverse engineered how to make certain passages do different things and how Twine worked as an engine and parser. More than anything gained from trying to understand the annoyingly crypt and frequently obtuse original API documentation, I was able to build from someone’s work for my own small projects.

That’s also why I was able to make my guides. What very little good I have been able to give back to the Twine community came as a direct result of Porpentine’s own posted code coming up on a year ago now. (Even though you will probably never read this, thank you for your continued progress toward weirdly wonderful, tragically twisted, and sinister silly everything too, Porpentine. May you forever shine on as a beacon for others to make whatever they want whenever they want for any reason they want and damn all others who would say otherwise.)

That’s why I am so excited about Twine finally having an official wiki! At last, the community can start to pool our knowledge together in an official and easy to find way. Instead of spreading tips, tricks, and hacks here and there across blog posts, pages, and various forums, we have a place to gather links and point out to how to help others get started. This is a fantastic opportunity to start building the tutorials and sharing code for others to come behind us.

Let down the rope, unfold the steps, lower the ladder, and reach out a hand. Now is the time. We are hurling towards a new year and the chance for beginning again. Before 2013 is out, if you have code, macros, or even advice for how others might do even the smallest thing in Twine, share it. Post links to the wiki and fill it to the brim with pages on everything that is currently able to be done in Twine and ways even more might be possible in the future.

Post your code.

Write a tutorial.

Share your knowledge.

Even just the experiences of frustration, something I’m not always personally willing to share myself, are important. We often fail before we succeed at what we want. We all fall down sometimes. By looking to others, those around us and the ones we look up to, we can see all the rough edges before the polish was applied. We can look behind the curtain and see what levers we might use ourselves for things. We can learn.

Here is a chance to prove its name. Twine: a bundle of threads connected together with purpose.


Filed under: #TwineTuesday, Twine Tuesday, video games Tagged: twine

#TwineTuesday: No-click Twine using jQuery

$
0
0

[This is my last #TwineTuesday post for awhile. I am taking a break from writing about Twine on a weekly basis to spend time on other teaching methods and projects.]

Last year, back when Twine was in its 1.3.5 version, it was hard to make a no-click project. As I covered in March 2013, it required some advance knowledge of how events were triggered across DOM elements and the differences between IE’s attachEvent and other browser’s use of addEventListener. It was all very complicated and messy.

Many people had turned to using jQuery as a solution to bypass all the browser differences too. And, while it was a great way to get no-click working, it required editing a Twine file after it had been compiled. This was something I felt broke the spirit of Twine projects and generally did not support or recommend for others to try.

However, now that Twine 1.4 can include jQuery internally, it has become much easier to do this. In fact, it only requires a handful of lines of code.

Jan28--1

From within Twine, go to Story -> Special Passages. Click on StorySettings to have that passage created for you (if it does not already exist).

Jan28--2

Change, if it hasn’t been already, the word “off” to “on” after the colon for jQuery. This will enable the use of jQuery code and syntax in the Twine project.

Create a script passage and copy the above code into it.

There you have it. Now, if a link that connects to another passage (an internal link) is created, it will be triggered by a ‘mouseover’ event. Just by moving the cursor over the link, it will be ‘clicked’ and tell Twine to load its associated passage next.


Filed under: #TwineTuesday, Twine Tuesday Tagged: jQuery, no-click Twine

Getting started with Twine on Ouya using Apache Cordova

$
0
0

The goal of this guide isn’t to teach you everything there is to know about running Twine projects working on the Ouya using Apache Cordova. Not only would that be pages and pages of potentially boring information, but it isn’t even something I can give. No, this guide has one simple goal: to get you up and running with Twine on the Ouya. It tries to cover the path I’ve taken myself and should, I hope, get you to a point where you can experiment with Twine on the Ouya without the same obstacles I had to overcome.

(Explicit Assumption 1: I’m also going to make the assumption that you are comfortable using the command-line. While much of this guide is composed of images taken from screenshots of various graphical interfaces, its final portion will consist entirely of entering command manually.

Explicit Assumption 2: You have already connected your Ouya to your computer via ADB. While I could cover this too, I spent considerable space to it within another guide, Getting started with CocoonJS on the Ouya. By searching for “Instructions for connecting with USB-to-MicroUSB cable (the hard way)” (if you have such a cable) or “Instructions for connecting via network (the easy way)” in that post, you can find detailed instructions. However, I will note, I do really recommend the network connection; it’s far easier to work with.)

To start, you will Node.js.

While its home page describes it as a “platform built on Chrome’s JavaScript runtime for easily building . . . applications,” a shorter definition might be the following three words: server-side JavaScript. Node.js has taken a language that was used primarily client-side in browsers, something used to make web pages fancy for example, and leveraged it to do desktop tasks and server processes.

step1

 

When you first visit the site, it should have guessed your operating system. Clicking on the “INSTALL” link will prompt you to download the correct installation file.

However, if you would like to pick your installer, clicking on the “DOWNLOADS” link will direct you to a listing of all the available packages.

 

step1.5

 

For this guide, I picked the installer to match my system. In this case, that was a 32-bit Windows installer.

 

step2

 

After clicking on the file, I was given the option by Firefox to save the file.

Depending on your browser, you may have been presented with a similar window. In any case, download and save the file.

 

step3

 

From within Firefox, I can select files I have downloaded. From your browser, find the installer you have now downloaded and execute it.

 

step4

 

If you are presented, as I was, with a security warning from Windows, you can safety click the “Run” button to continue.

 

step5

 

Once the setup application had configured itself, it will present you with the option to click “Next”. Do so.

 

step6

 

As with all software, this too comes with a End-User License Agreement. Take the time to read through it.

 

step7

 

After the EULA, you can pick where you would like Node.js to be installed. If you do not have a preference, or are fine with its default selection, click “Next” to continue.

 

step8

 

Once presented with the installation options for Node.js, you can select and customize what you would like.

Unless you really want to change things, you can click “Next” to continue.

 

step10

 

Now that the setup application has configured itself and recorded your choices, it is now ready to install Node.js. Click “Install” to do so.

 

step11

 

After some amount of time, it will finally complete its process. Once done, you can click “Finish” to close the setup application.

 

Now that Node.js is installed, we need to get Git.

 

step13

From its home page, it too should have guessed at your operating system.

Clicking on the “Downloads…” click should prompt you to download its installer.

 

step14

 

Like before with Node.js, save the file to a location.

 

step15

 

After it has downloaded, just like before, execute the file to start the installer.

 

step16

 

If you are given another security warning, click “Run” again.

 

step17

 

Once it starts, the setup application for Git will give you the option to click “Next >”. Do so.

 

step18

 

The installer for Git has it own EULA. Take the time to read through this as well.

 

step19

 

Once given the ability to choose how to install Git in your system, you can customize it to your liking.

 

If you have no preference, click “Next >” to continue.

 

step20

 

Under the “Adjusting your PATH environment” options, you can choose between two possible options for this guide.

Pick either the center, “Use Git from the Window Command Prompt”, or bottom-most, “Use Git and optional Unix tools from the Windows Command Prompt” option.

 

step21

 

If you have a PuTTY session in your registry (as I did), you can choose to use that option.

If you did not, or have no preference, select “Use OpenSSH” and click “Next >” to continue.

 

step22

 

As the last of the options, the Git installer will prompt you to select how you would like to end text files.

 

If you have no preference, click “Next >” to finish configuring its options.

 

step23

 

After some period of time, Git will finally be installed.

 

If you would like to read the release notes, select that box once given the ability. If you would rather not at this time, un-select it before clicking “Finish” to close the installation application.

 

Now that both Node.js and Git are installed, we will spend some time using Apache Cordova from its command-line interface.

 

step24

From within Windows, a command-line window can be accessed by typing “cmd” within the application search bar and pressing ENTER.

 

step25

Once the command-line window is open, enter the following “npm install -g cordova”.

 

Note: This will install ‘cordova’ through the Node Package Manager using the global (-g) flag.

 

step26

 

Depending on what other NPM modules are installed on your system, the installation of Cordova could be very quick or take a great deal of time.

 

step28

After Cordova has been installed, we can create a “Hello World” (traditional first program).

 

To do that, enter the following: “cordova create hello com.example.hello HelloWorld”.

Note: It will tell Cordova to create a folder (“hello”) containing the project “com.example.hello” (named using the reverse-domain method) and make the main process “HelloWorld”.

 

step29

Once it has finished creating the project, change the directory to ‘hello’ using the command “cd hello”.

 

step30

As part of an upcoming step, we will need to install a plugin to Cordova. Before we can do that, however, we need to install another Node module.

 

This time, enter the following: “npm install -g plugman”

 

step30-1

Now that we have a blank Cordova project created, we can begin to populate it by adding a platform to it.

 

Because the Ouya is an Android-based device, we will use the “cordova platform add android” command from within the “hello” folder.

 

step32

Once it is finished, we will have nearly all of the tools installed and ready to go.

 

The last is one plugin, is a “Android TV Plugin for Cordova”. It patches in the necessary Android configurations to tell Ouya that the compiled program is a ‘game’ and not another type of application.

 

step33

To install it, enter the following:

cordova plugin add https://github.com/hughisaacs2/Cordova-Android-TV-Plugin.git

 

Now, finally, we have Cordova configured and ready for our Twine file.

 

Open Twine and, for the sake of this guide, create a new Story.

 

step34

 

If wanted, save the story as a file for later editing.

 

step35

 

Once ready, click on Build -> “Build Story…”

 

step36

 

Given the choice for where to save the file, find the the “hello” folder created by Cordova. Inside of that, enter the “www” folder and select the “index.html” file. We will be overwriting this file with the contents of the Twine story.

 

step37

 

 

Windows will present a prompt telling you that the file already exists. Click “Yes” to replace the Cordova created file with the Twine one.

 

step38

 

Now that everything is ready, return the command-line window. Assuming you have already connected your Ouya to your computer via ADB (as I stated at the beginning of this guide), type and run the following command “cordova run”.

 

step39

 

This will build the project, compile it into a APK file, install it on the remote system, and tell it to run immediately.

 

step40

 

If all processes have run correctly and not reported any errors, you will see the final message “LAUNCH SUCCESS” in the console.

 

My Great Capture Screen Shot 2014-07-31 03-34-45

 

Turning to the Ouya-connected screen, you should now see the Twine file running on the Ouya.

 

You are now ready to experiment using Twine on the Ouya with Apache Cordova!

 

Additional Notes:

  • As is maybe obvious, you can substitute your own Twine files for the default file used in this guide. All that is needed is that it be placed in the “www” folder and (re-)named “index.html” for Cordova to compile it into a project.
  • Each new execution of “cordova run” will re-install the project. While you can manually uninstall each project using the Ouya’s menu, you do not have to with each new change.
  • You can navigate the links on the page using the left joystick. Pressing “O” will select that link; pressing “A” is the same as pressing BACKSPACE, it will go back one link within the history of the page.
  • For a more permanent project, consider creating a new project with a different name than “HelloWorld” and changing the icons. For the latter, consult the documentation.

Filed under: #TwineTuesday, code, howto, Twine Tuesday Tagged: Apache Cordova, Git, Node.js, twine

#TwineTuesday: Hosting Twine files with philome.la

$
0
0

Now that Google Drive has changed and Twine files can no longer be freely hosted using that service, it is time to turn to some alternatives. The first among those, and one of the most popular current options, is to use philome.la. It’s a site designed for the sole purpose of hosting Twine files. However, it does come with some caveats I’ll get into as I go over this helpful service.

Before you can start to use philome.la, though, you will need a Twitter account. As of this writing, this is a mandatory step. Make sure you have one before continuing!

 

phil_1

 

As soon as you visit philome.la, you’ll notice its simple instructions. The first of which is to “Login with Twitter.” Click on “Login” to start this process.

 

phil_2

 

If you are not logged into your Twitter account before clicking on “Login”, you will be prompted to do so.

Once done, you will be shown two options: “Sign In” or “Cancel”. After reading through what philomela will be able to access and what it won’t, and only if you agree to give it access, click “Sign In” to continue.

Note: If at any time you want to revoke access to your Twitter account for philomela (or any another application), visit your Settings -> Apps on Twitter.

 

phil_3

 

After philomela has access to your Twitter account, you will notice that you are logged in under that name. Now, you are ready to upload your built file — and nearly done with the whole process too!

To start the upload, you need to follow the instructions exactly. You must drop the file on top on the webpage.

 

phil_4

 

If you have never dropped a file before, simply resize the webpage and open the directory where the file is located. If that window is full-screen, resize it so that it overlaps with the webpage. From the open directory, click on the file and drag it over to the webpage. Once you see the cursor change and are over the “Drop your file here” are on the webpage, let go.

 

phil_5

 

If successful, you will see the name of the file and its size within the box that previously read “Drop your file here”. This means it is now uploaded and awaits a naming.

 

phil_6

Naming your game will dictate how it appears to others as a URL.

(For example, I named my game “Cnossus” which means it will show up like the following:

phil_7

The name is the last part of the URL, with your Twitter account name after the site’s name followed by a slash and then its name.)

 

phil_8

 

Before you click on “Publish!”, look at the check-box underneath it. If you would like your game to show up in the @philomela_twine feed, leave this box alone. However, if you would rather not announce it yet, click on the box to un-check that option.

Once ready, click on “Publish!”

 

phil_9

 

Congratulations!

Your Twine game is now published on philomela and ready for anyone to play.

From this page, you can share the URL or click on the “Tweet” button to open a new window and craft your own Twitter announcement.

 

Caveats:

As I wrote at the top of this post, philomela does come with some caveats. One of the most obvious ones is this: it’s a service designed around hosting Twine files. It isn’t a full webserver service or free hosting for multimedia files. Anything that isn’t represented as text within a HTML file itself cannot be hosted with philomela. It isn’t built for that. Any external images or sounds need to be hosted elsewhere.

Removing (and then updating) games can also be a little confusing at first too.

To do that, look for the text at the bottom of the webpage.

 

phil_10

 

Clicking on the “manage/delete your games” will direct you to a listing of your games hosted under your Twitter account with philomela.

 

phil_11

 

From here, if you want to delete one, move your cursor after the number of plays.

 

phil_12

 

Clicking on the “X” will produce a confirmation message.

 

phil_13

 

If you are sure you want to remove the file, click on “Yes”. If you want to keep it, click “No”.

As of this writing, removing and re-uploading files is the only way to “update” versions of a game.


Filed under: #TwineTuesday, Twine Tuesday Tagged: philomela, twine

#TwineTuesday: Packaging Twine with Node-Webkit

$
0
0

While usually one of the greatest strengths of Twine is is ability to be run nearly anywhere because of its use of the HTML format, there are some times when you might want to package your files together for distribution. Instead of sending out a URL, for example, you can put all of your resources like images, sounds, and fonts into one single file and then use that single package as your project. In fact, by pairing that package with its own web browser, you can gain more assurance over what a user might see and hear and can better craft the overall experience for them.

To that end, I’ll be covering how to use one of the more popular options for packaging web projects for desktops: Node-Webkit.

Note: This guide will reference using the command-line. If you intend to follow this guide, make sure you are comfortable using that interface.

Note: Because the computer I am writing this on is a Windows machine, this guide will also reference Windows functionality and screenshots. However, Node-Webkit does support both Mac OSX and Linux too.

Introduction

To those unfamiliar with either libraries, let me take a moment here to explain both and the reason for the hyphen.

The first part of the name, Node, comes from Node.js, a increasingly popular way of using JavaScript as a scripting language outside of web browsers. With Node.js, instead of only using JS on a client side, you can leverage it to act as both a web server and the programming to retrieve data from a database too. As well, many developers (including myself) also use it as a general purpose scripting language for taking care of tasks where typing the same commands over and over would be frustrating or as a way to regulate processes with many steps that can be easily combined together.

The second half of the name, Webkit, is “open source web browser engine” that runs many of the applications like Safari (on all iOS devices), Mail, and Dashboard in Mac OSX. It was also, until more recently, the engine behind Google’s Chrome browser too. It’s an engine that has been around for a number of years and has seen a great many improvements in speed and rendering optimizations.

The name is hyphened, as you might imagine, because the project is a combination of the two libraries. It takes the flexibility and scripting ability of Node and merges it with the browser engine of Webkit. It’s an easy way, as I wrote at the top of this post, to package sites and projects that would have run online through a web browser to now have a web browser as part of the single file distributed together.

Getting Started

To start, we will need the Node-Webkit binary files.

twine-node-1

 

To get these, visit its GitHub page and scroll down to the Downloads section.

Under the “v0.10.2″ bullet, click on the version that matches your operating system. (For this guide, that will be Windows, so click on the “win32″ build link.)

 

twine-node-2

If you are using Firefox or a similar browser, you can open a list of recently downloaded files and select the node-webkit ZIP file from there. (For Chrome and other browsers, this download might be found on the bottom of the page.)

Regardless, find the file and click on it.

 

twine-node-3

 

Find the “Extract all files” button.

twine-node-4

 

Click on “Browse” to select the path to extract the files to or, if you have no preference, extract them to the same directory as they were downloaded. (If you prefer not to open the folder after the files are extracted, leave the “Show extracted files when complete” option marked.)

Click on “Extract” to begin the extraction.

 

twine-node-5

Once extracted, open the folder. In it, you should see the “nw.exe” file as well as many others. This is the compiled, binary version of Node-Webkit.

 

To use it, we will need to create a “package.json” file that will be the instructions for how Node-Webkit will operate.

 

twine-node-6

One way to create this file, and the way I will go over, is to create a new text file first.

From the right-click context menu (opened by right-clicking inside a folder), go to New -> Text Document.

 

twine-node-7Once this file is created, open it.

 

Copy and paste in the following into the file:

{
  "name": "Twine",
  "main": "cnossus.html",
  "window": {
		"toolbar": false
	}
}

twine-node-8

Note: I plan to use my “Cnossus” Twine project as the example here. You will need to change the name to match whatever file you plan on using.

 

twine-node-9

Once you have copied and pasted the text from above, as well as changed the file name to match your own, it is now time to save the file. (If needed, you can always edit it later too.)

To create the package.json file, go to File -> “Save As…” from the menu. Within the File name field type the following exactly: “package.json”

(Be sure to include the quotation marks!)

 

twine-node-10

If the file was created correctly, there should now be a “package.json” file within the directory and Notepad should have changed its title to “package”.

Now, we need to move our Twine file over.

twine-node-11

Either by saving into the folder directly or by copy-and-pasting it, move a HTML file into the same directory. (For this guide I’ll be using my Cnossus file for all screenshots.)

Once it has been moved over, it is now time to test the files before packaging them together.

twine-node-12

To test the files, we will need a command-line window. To open one, type in “cmd” in the Program FIles menu and press enter.

 

twine-node-13

To navigate to the directory with our Node-Webkit files, return to the open directory in Explorer.

Click on the file URL at the top. Once it becomes text, right-click on it and select Copy from the menu.

 

twine-node-14

 

Return to the command-line window.

 

twine-node-15

Type “cd” and right-click on the window. Select Paste.

Press Enter.

twine-node-16

Now, finally, we are ready to test the files.

twine-node-17Type the following: nw .

(That’s the command “nw” followed by a space and a period.)

Press Enter.

twine-node-18

If everything worked correctly, you should see your Twine project opened in the Node-Webkit window.

Congratulations!

 

Packaging Files

After testing your files, changing settings, or otherwise getting your project ready, it is time to package your files together.

To do that, you first need to ZIP the files together.

From the directory containing your project, select the files you want to zip together (including package.json) and right-click on them. Select Send to -> Compressed (zipped) folder.

 

twine-node-19This will create a ZIP file with its name as the last file selected. (In this guide, that will be “package.json”.)

Once created, let’s test it one more time.

 

twine-node-20

To do that, run “nw package.zip”

(Assuming everything looks and works as it should, it is time to move to the last command.)

 

twine-node-21

 

As the last step, combine the Node-Webkit binaries with your ZIP file.

The command to do that is: “copy /b nw.exe+package.zip app.exe

(If you wish to change the final executable name, you can change it to something other than “app” here too.)

Press Enter.

twine-node-22

If everything worked correctly, a new file should have been created in the folder matching the name of the executable you choose (or “app.exe” by default).

Your Twine project is now packaged with Node-Webkit!

 

Distribution

In order to run, your packaged application will need both the “nw.pak” and “icudt.dll” files. Be sure to include those two files.

If you are are using video or audio, you will also need the “ffmpegsumo.dll” file.

Finally, although unlikely in a Twine project, the “libEGL.dll” and “libGLESv2.dll” files are needed for for WebGL support in Webkit.

 

Additional Notes

  • While I used the simplest example in this guide, there are more options for package.json and example code to do much more on the Node-Webkit wiki.
  • When packaging files using Node-Webkit, consider if the end user will have a network connection or not.  If you are using Google Web Fonts (as my example Twine project did), they may not load without a network connection. Therefore, it is always best to include everything a user might need as part of the package.

Filed under: #TwineTuesday, Twine Tuesday Tagged: Node-Webkit, twine

#TwineTuesday: An Introduction to Twine 2.0 (video)

$
0
0

Covering many of the changes from previous versions and introducing some of the new functionality in Twine 2.0, this video is targeted mostly at users who have seen or used Twine before, but haven’t yet moved over to the new version.

Highlights:

  • Passage Management
  • Using HTML elements
  • Overview of the new Hook system with “click” and “click-replace”

Check out Twine’s website, Twine 2.0, or a complete breakdown of all new and changed functionality for more details.

 


Filed under: #TwineTuesday, howto, Twine Tuesday Tagged: Twine 2.0

#TwineTuesday: Twine 2.0: Assignment and Value Macros (video)

#TwineTuesday: Twine 2.0: Hooks (video)

#TwineTuesday: Twine 2.0: Changer and Sensor Macros (video)

#TwineTuesday: Twine 2.0: Using CSS (video)

#TwineTuesday: Twine 2.0: Using JavaScript in different story formats (videos)

$
0
0

Harlowe


An overview of using JavaScript in the Twine 2.0 story format Harlowe. Details how to call the built-in JavaScript functionality from TwineScript, and how to use the “Edit Story JavaScript” window.

Snowman


An overview of using JavaScript in the Twine 2.0 story format Snowman. Details how to create user-defined functions, run them, and the various commons issues with function scope between passages and from the “Edit Story JavaScript” window.

SugarCube


 
An overview of using JavaScript in the Twine 2.0 story format SugarCube. Details how to create user-defined macros, run them, and how, generally, to invoke other macros in the SugarCube format.

 


Filed under: #TwineTuesday, Twine Tuesday Tagged: Twine 2.0
Viewing all 43 articles
Browse latest View live