Sunday, October 13, 2013

Displaying an array of images in Twine

Hi all

I've been doing a lot of Twine games lately (one a day for the month of October).  I'm going to blog some of the lessons learned.  One of the techniques I figured out was displaying an arbitrary number of images from an array.  I was making a tarot card game where three cards are selected and shown.  I didn't want to make a large "if else" structure to display the selected cards so I came up with this.

First off take a look at the end result.

Here is the twee code

::Start
<<display actualStart>>

::StoryTitle
Spooky Fortune

::StoryAuthor
Murph


::actualStart
<<set $imgCnt = 0>>\
<<set $card1 = Math.floor(Math.random()*22) + ".jpg" >>\
<<set $card2 = Math.floor(Math.random()*22) + ".jpg" >>\
<<set $card3 = Math.floor(Math.random()*22) + ".jpg" >>\
<<set $images = [$card1, $card2, $card3]>>\
<<set $fortunes = ["A ghost will perform standup for you!!!", "A werewolf will sell you insurance", "Vampires will look at you funny", "A Frankenstein Monster will make you pancakes", "The creature from the black lagoon will eat your sandwhich", "A zombie will gossip really bad about you", "A demon will serve you in Applebees", "A skeleton will pee next to you in the bathroom", "Share a drink with a salaad", "A gargoyl will fart near you", "A slime blob will leer at your wife", "A cerebus will ask you out on a date", "A robot will encourage you on your job", "An ilithid will sell you shoes", "A moleman will offer you a drink", "A mad genius will build you a shed", "A pagan god will mow your lawn", "A mutant will deliver a pizza", "A two headed shark buy something from you on Craigslist", "A kraken will invite you to a party", "A poltergeist will try and sell you knives", "A black knight will accidently call you", "A merman will be your interior decorator", "A dinosaur will sell you a hot dog", "You will cause a mummy to get fired", "Flesh eating beetles will sit next to you on the bus", "You will read a really good book written by a yeti", "A dragon will compliment your hat", "The boogie man will give you a motivational speech", "Bloody Mary will polish your silverware", "The Stay Puff Marshmellow Man will jump your car", "Chucky will say hello", "Freddy will bump you in the hall", "Jason will hand you a brochure", "You will share a cab with a kappa", "Tengus will go on a pinic with you"]>>\
<<set $f = Math.floor(Math.random()*$fortunes.length)>>\
<<display cards>>
<div class="vvv">
<<print $fortunes[$f]>>
</div>

::cards
<<if $imgCnt < $images.length>>\
<<set $i = "[img[">>\
<<set $i = $i + $images[$imgCnt]>>\
<<set $i = $i + "]]">>\
<<print $i>>\
<<print "   " >>\
<<set $imgCnt = $imgCnt + 1>>\
<<display cards>>
<<endif>>

::breaking [script]
(function(){
var bs = String.fromCharCode(92);
Wikifier.formatters.unshift({
    name: "continuedLine",
    match: bs+bs+"s",
    handler: function(a) {
        a.nextMatch = a.matchStart+3;
    }
});
}());

::usehtmlstuff [script]
(function () {
var bs = String.fromCharCode(92);
Wikifier.formatters.unshift({
name: "htmltag",
match: "<"+bs+"w+(?:(?:"+bs+"s+"+bs+"w+(?:"+bs+"s*="+bs+"s*(?:"+'"'+".*?"+'"'+"|'.*?'|[^'"+'"'+">"+bs+"s]+))?)+"+bs+"s*|"+bs+"s*)"+bs+"/?>",
tagname: "<("+bs+"w+)",
voids: ["br", "hr", "area", "img", "input", "embed", "param", "source", "track"],
handler: function (a) {
var re, tn, e;
re = new RegExp(this.tagname).exec(a.matchText);
tn = re && re[1];
if(tn) {
e = document.createElement(a.output.tagName);
e.innerHTML = a.matchText;
e = e.firstChild;
if(this.voids.indexOf(tn.toLowerCase()) == -1) {
a.subWikify(e, "<" + bs + "/" + bs + "s*" + tn + bs + "s*>");
}
a.output.appendChild(e);
}
}
});
}());


::stylesfortarot [stylesheet]
img
{
   width:300px;
   height:525px;
   align="middle";
}



.vvv
{
   font-family: Consolas, monaco, monospace;
   font-style:italic;
   font-size:40px;
   text-align: center;
 
}

So lets address some things.  ::breaking and ::usehtmlstuff are not my code.  Credit goes to L from Glorious Trainwrecks.  ::breaking replaces <<silently>> with a \, you can find it here.  ::usehtmlstuff is so I can place my divs for css easier, you can find it here

Lets break down ::actualStart

<<set $imgCnt = 0>>\
<<set $card1 = Math.floor(Math.random()*22) + ".jpg" >>\
<<set $card2 = Math.floor(Math.random()*22) + ".jpg" >>\
<<set $card3 = Math.floor(Math.random()*22) + ".jpg" >>\

We initialize $imgCnt to 0.   This will be used later.  We then randomly generate a card (since this is tarot there are 22 cards) and append a .jpg to get an image filename.  1.jpg through 22.jpg are all images I have uploaded.

<<set $images = [$card1, $card2, $card3]>>\

Now we put the three image filesnames into an array.  In this case it's three cards, but this can be an arbitrary number.

<<set $fortunes = ["A ghost will perform standup for you!!!", "A werewolf will sell you insurance", "Vampires will look at you funny", "A Frankenstein Monster will make you pancakes", "The creature from the black lagoon will eat your sandwhich", "A zombie will gossip really bad about you", "A demon will serve you in Applebees", "A skeleton will pee next to you in the bathroom", "Share a drink with a salaad", "A gargoyl will fart near you", "A slime blob will leer at your wife", "A cerebus will ask you out on a date", "A robot will encourage you on your job", "An ilithid will sell you shoes", "A moleman will offer you a drink", "A mad genius will build you a shed", "A pagan god will mow your lawn", "A mutant will deliver a pizza", "A two headed shark buy something from you on Craigslist", "A kraken will invite you to a party", "A poltergeist will try and sell you knives", "A black knight will accidently call you", "A merman will be your interior decorator", "A dinosaur will sell you a hot dog", "You will cause a mummy to get fired", "Flesh eating beetles will sit next to you on the bus", "You will read a really good book written by a yeti", "A dragon will compliment your hat", "The boogie man will give you a motivational speech", "Bloody Mary will polish your silverware", "The Stay Puff Marshmellow Man will jump your car", "Chucky will say hello", "Freddy will bump you in the hall", "Jason will hand you a brochure", "You will share a cab with a kappa", "Tengus will go on a pinic with you"]>>\
<<set $f = Math.floor(Math.random()*$fortunes.length)>>\

I got lazy and didn't add code to interpret the cards.  This code picks a random fortune for display.

<<display cards>>

We have to <<display cards>> at this point because we need a loop.  Loops (like for or while) are something twee and twine do not do easily, but we can fake it.

::cards
<<if $imgCnt < $images.length>>\
<<set $i = "[img[">>\
<<set $i = $i + $images[$imgCnt]>>\
<<set $i = $i + "]]">>\
<<print $i>>\
<<print "   " >>\
<<set $imgCnt = $imgCnt + 1>>\
<<display cards>>
<<endif>>

Here we are faking a loop.  Remember when we initialized $imgCnt to 0?  This is why.  If this was javascript this would be a while($imgCnt < $images)
The basic idea is to construct a variable, in this case $i, to hold the twee code for the image that we want to display.  If we wanted to display "1.jpg", at the end of the day $i will hold "[img[1.jpg]]]".

<<set $i = "[img[">>\
<<set $i = $i + $images[$imgCnt]>>\
<<set $i = $i + "]]">>\
<<print $i>>\
<<print "   " >>\
Lets dig deeper into these three lines.  The first line sets up the image tag.  The second line gets the value from the array that contains the name of the images.  The third line closes the image line.  Finally we print the whole "img" tag.

<<set $imgCnt = $imgCnt + 1>>\
<<display cards>>
<<endif>>

This is the rest of the while loop.  We increment $imgCnt, and "loop" back to start of this node.
That's about it.


I can see this used for things other than cards are inventories, held in an array, can hold and lose a arbitrary number of things