Thursday 7 May 2015

Making a basic game in Twine

We are back into the land of mostly words with Twine: http://twinery.org/. The latest version is 2.0.4 and all work is done online.  I am using Twine 1.4.2 because I’m old and curmudgeonly Smile.

You can play the the created game online here: http://www.thecatsweb.com/tutorials/LightMyFire/Twine/

You can download the source code here: http://www.thecatsweb.com/tutorials/LightMyFire/Twine/light.tws

Twine is excellent for making word heavy Choose Your Own Adventure style games playable on a browser. There are some excellent tutorials online on how to get started, so I’m only going to cover the basics, and what I did to make my game more like a Point and Click adventure.

image

We you first start up Twine, you are met with a screen with three “passages” on it.  A passage is the basic building block in Twine.

The Start passage is where your player will “start” their story.

StoryTitle and StoryAuthor are special named passages.  They are used in the website template.

First thing I need in my game is some sort of inventory system. A quick search on Google let me to a page featuring an inventory system created by Xax. I create a new passage called “inventory” and tag it with “util” for bookkeeping purposes.

You have:\
<ul>\
<<if $inventory.length gt 0>>\
<<set $_i to 0>><<inventory_>>\
<<else>><li>Nothing</li><<endif>></ul>

Things to notice:

  • You can add html to you passages
  • <<>> mark codes
  • $inventory is an array that I will declare in “Start”

So I check to see if there are any items in the inventory array. If so, I set a variable $_i to 0 and call on the passage called <<inventory_>>. Otherwise, I print “Nothing”.

<<passage_name>> is a shortcut that means print the contents of the “passage_name” passage here.

Next passage is “inventory_”

<<if $_i lt $inventory.length>>\
<li><<print $inventory[$_i]>></li>\
<<set $_i to $_i + 1>><<inventory_>>\
<<endif>>

This passage check to see if $_i is less than $inventory length, and if so, print the inventory item. It then increases $_i by one and calls the passage again.

Now to work on the story.  I decided that there are three types of passages:

  • locations: represent the physical locations and are the hub for the other two types.
  • flavour: these are passages that describe the setting in greater detail.
  • action: these are actions that the player can take.

Start passage:

You have been left outside a cabin in the mountains.  You need to make a fire so you don't freeze to death.

[[Start Your Adventure|exterior]]
<<set $inventory to []>>
<<set $woodtaken to false>>
<<set $woodplaced to false>>
<<set $matchseen to false>>
<<set $woodlit to false>>

Here is where I set up my inventory and all my switch variables.

Sample location – Interior:

Cozy is the first thing that comes to your mind when looking around the interior. Tiny also comes to mind.

There is an open [[fireplace]] opposite the door and a [[table]] and [[chair]] set up by the [[window]].

<<inventory>>
You can:\
<ul>\
<li> [[Leave the cabin|exterior]] </li>\
<<if $inventory.indexOf("wood") != -1>><li> [[Put the logs in the fireplace.|put_logs]]</li><<endif>>\
<<if $matchseen and $inventory.indexOf("match") == -1>><li> [[Take a match from the table.|take_match]]</li><<endif>>\
<<if $woodplaced and $inventory.indexOf("match") != -1>><li> [[Light the logs in the fireplace.|light_wood]]</li><<endif>>\
</ul>

First of all, I write out the description of the location. The words in square brackets [[]] link to the flavour passages with that name.  Then I print out the inventory and the actions available at this location. The links that look like this: [[Leave the cabin|exterior]] mean to have the words “Leave the cabin” link to the passage called “exterior”.

The next three lines only appear when the right conditions are met.  $inventory.indexOf("wood") != –1 this checks to see if the player has any wood in his inventory.

Sample flavour – Wind

A bitter cold wind that would gladly take you away.
<<return>>

The <<return>> macro just makes a link back to the preceding passage.

Sample action – Take Wood

<<set $inventory.push("wood")>>
<<set $woodtaken = true>>
You take the pile of logs.

<<return>>

set $inventory.push("wood") puts some wood in the players inventory. Then I set the variable $woodtaken to true.  Finally I provide feedback to the player and a link back to the previous passage.

Here is the final layout of my game:

image

Twine is great for writers who don’t know much about programming.  You can write a great interactive fiction with barely touching any code.  (Even less than what I have here!)  The interface is very visual and you can move passages around any way they make sense to you.

Other articles in this series:
Making a Basic Game with Inform 7
Sample Game with JSGAM
Making a Basic Game in Twine

Friday 1 May 2015

Sample Game with JSGAM

This is the second in the series where I make a simple game.  Like last time, you are left outside a winter cabin and you have to light a fire to survive.  There is firewood left outside and matches and a fireplace inside.

To play the game: http://www.thecatsweb.com/tutorials/LightMyFire/JSGAM/
To get the source files:http://www.thecatsweb.com/tutorials/LightMyFire/JSGAM/light.zip

This time I am using JSGAM - an open source Javascript game engine focused on classic graphical point-and-click adventure games. From their homepage: http://jsgam.sourceforge.net/ JSGAM provides a great framework for making Point and Click adventures in Javascript.  It handles menus, saving, inventory, and other things.
First thing you need to do is set up a directory that will hold all the necessary files.  The easiest way to do this is to copy an existing project and delete the files you don’t need.
screenshot_directories
Next step is to open your “js\screens\title.js”

function Main()
{
  //Title of the Room
  SetTitle("Light my Life");
 
  CreateScreen("title");
  FirstScreen="exterior";
 
}

This sets up the first screen that people will see and the screen that people go to when they hit the Home button. SetTitle – sets the browser’s title bar. CreateScreen – tells the engine what image to use as a title screen. FirstScreen is the first screen of the game.
Like most Point and Click games, your game is split into different screens.  There is one javascript file for each screen.  This game really only has to locations: inside and outside the cabin.

After the title is set, you can make your first screen called exterior.js:

function Main()
{
    //Title of the Room
    SetTitle("Exterior");
   
    //Image background and walkable area
    CreateScreen("exterior", "152,398,269,223,591,220,706,396");
   
    //Adding the player
    CreatePlayer(450,300);
}

The numbers in CreateScreen lets the engine know what the walkable area is. CreatePlayer uses the Sprites in the Player directory as the player.

You can now run the game.  But it is pretty boring. You can walk around the screen, but nothing else.  Let’s add some stuff to look at:

TheWoods=CreateInvisibleObject("185,9,296,221,596,214,732,396,799,399,797,0,440,4");
TheWoods.description="Lots and lots of snow covered trees.";
TheCabin=CreateInvisibleObject("114,6,219,109,202,132,83,305,0,192,2,129");
TheCabin.description="I think it might be warmer in the cabin.";
 
TheDoor=CreateInvisibleObject("126,377,126,266,159,219,159,319");
TheDoor.description="This is the door into the cabin.";
TheWindow=CreateInvisibleObject("170,205,175,237,195,217,190,181,171,206");
TheWindow.description="It looks dark in there.";

CreateInvisibleObject makes a shape around something in the background.  If the player looks at it, the game will say the description.

Now you might be wondering where I’m getting those numbers.  I am cheating and using an image map generator. Similar to this one: http://imagemap-generator.dariodomi.de/
imagemap_example
Easy peasy!
Now to add the next room.  We make a screen file called interior.js with the following:

function Main()
{
    //Title of the Room
    SetTitle("Interior");
   
    //Image background and walkable area
    CreateScreen("interior", "182,240,641,240,771,399,5,397,29,357,111,356");
   
    //Adding the player
    CreatePlayer(645,260);
     
    TheTable=CreateInvisibleObject("22,325,110,326,181,210,105,209");
    TheTable.description = "A solid wooden table.";
    TheRug=CreateInvisibleObject("231,290,283,263,390,251,496,261,553,293,531,321,448,340,342,341,271,325,232,303");
    TheRug.description="A rustic braided rug.";
    TheDoor=CreateInvisibleObject("665,95,755,120,755,375,667,267,667,97");
    TheDoor.description = "The door back to the frigid outside."
    TheDoor.DoorTo("exterior",150,300);
}
And change the door in exterior.js to:
TheDoor=CreateInvisibleObject("126,377,126,266,159,219,159,319");
TheDoor.description="This is the door into the cabin.";
TheDoor.DoorTo("interior");
This links the two doors and allows the player to walk between them.
Now I’m going to place the wood in exterior.js and the match in interior.js.
Wood=CreateObject("Wood",365,245);
Wood.description="A pile of well dried logs.";
Wood.Takable();
Match=CreateObject("Match",90,237);
Match.description="A pretty match.";
Match.Takable();

To create the object, you need to make a directory in the Sprites folder the uses the same name.  In this folder, you put an image of the object called “left.gif”. Takable indicates that you can take this item.

Magic! You can run it again and try out taking the items.

Now is the tricky part. Letting the fireplace know about the wood.

    TheFireplace=CreateInvisibleObject("264,76,264,235,515,235,515,76");
    TheFireplace.description = "A lonely empty (and cold) Fireplace.";
    TheFireplace.UsableWith("Wood",UseWood);
   
    Wood=CreateObject("Wood",365,205);
    Wood.description="A pile of well dried logs.";
    Wood.Hide();
    Wood.UsableWith("Match",UseMatch);
   
    if(SearchParameter("LogInFireplace")){
        TheFireplace.description = "A fireplace with logs in it.";
        Wood.Show();   
    }   
[…]
function UseWood(){
    RemoveInventoryObject();
    CreateParameter("LogInFireplace");
    Wood.Show();   
}

UsableWith lets the object know what objects to listen for use actions. Then which function to call if it is used. I make a “ghost object” (that’s what I call it) of the wood in the fireplace and hide it right away. The page then looks to see if the LogInFireplace parameter has been set in place yet.  If so, the page changes the description of the fireplace and shows the wood. The UseWood function removes the wood from the inventory, creates the LogInFireplace parameter, and show the wood object that is hiding in the fireplace.

Halfway there! (Object-wise)

Now for the Match

    Fire=CreateObject("Fire",365,160);
    Fire.description="What a lovely fire!";
    Fire.Hide();

    if(SearchParameter("FireLit")) {
        Fire.Show();
    } else if(SearchParameter("LogInFireplace")){
        TheFireplace.description = "A fireplace with logs in it.";
        Wood.Show();   
    }      
Now I created a ghost object of the Fire that checks for the FireLit parameter. And finally, the UseMatch function that is attached to the Wood object.
function UseMatch(){
    RemoveInventoryObject();
    CreateParameter("FireLit");
    Wood.Hide();
    Fire.Show();
    Player.Say("Now I have a great fire!", 5000,function () { delete_cookie("LastScreen");GotoScreen("title"); } );
}
I had to modify the Say function in the js/engine/action.js file
function Say(txt,time,do_next)
{
    if(this.SayTxt==undefined)
    {
        if(time==undefined) var time=delay;
        this.SayTxt=new CreateText(parseInt(this.style.left),parseInt(this.style.top),this.divname);
        this.SayTxt.ChangeText(txt);
        this.setImage(this.status+"speak");
        var tmpthis=this;
        tmpthis.lockSprite=true;
        setTimeout(function(){
            DestroyText(tmpthis.SayTxt);
            tmpthis.SayTxt=undefined;
            tmpthis.lockSprite=false;
            tmpthis.setImage(tmpthis.status.substring(0,tmpthis.status.length-5));
            if (do_next!=undefined) do_next();            },time);
    }
}

I couldn’t figure out how to do an end screen.  So I modified the Say command to take a function to run after the character is done talking.  So after the Player says "Now I have a great fire!", it waits 5000 ms, clears the LastScreen cookie, and goes to the title screen. The LastScreen cookie is how it determines if there is a current game.

That’s it for now.  Remember you can get the source files here: files:http://www.thecatsweb.com/tutorials/LightMyFire/JSGAM/light.zip

Other articles in this series:
Making a Basic Game with Inform 7
Sample Game with JSGAM
Making a Basic Game in Twine

Writing a basic game with Inform 7

This series covers the creation of a simple Point and Click style adventure as created using various engines, I am using the term "Point and Click" very loosely since some of the engines I am going to  use are text based.

The set up is simple.  You start out outside a cabin situated in the mountains.  You have to make a fire or else you will freeze to death.  There are two locations: outside and inside the cabin.  Outside the cabin is a pile of firewood.  Inside the cabin is a fireplace and a match.  You need to put the firewood in the fireplace and light it with the match.  Not one of your harder puzzles.

The first engine I am going to demonstrate is Inform 7. http://inform7.com/  Inform helps you create text based adventures or, more recently, Interactive Fiction or IF.  Version 7 digresses from its predecessors by creating the game using plain English instead of a programming language.

The final version can be played here: http://www.thecatsweb.com/tutorials/LightMyFire/Inform7/ (Please note that it includes a JavaScript parser called Parchment that lets you play it in your browser.)

First thing I'm going to do is set up my two locations:

"Light my Fire" by Susan Douglas

Section 1 - Locations

Exterior is a room. "You find yourself outside a small wooden [building] to the west.  The artic [wind] cuts through your clothing sapping what little heat you've managed to conserve."

Interior is west of Exterior. "Cozy is the first thing that comes to your mind when looking around the interior.  Tiny also comes to mind.  There is an open [fireplace] opposite the door and a [table] and [chair] set up by the [window]."


If you tried to run the code as is, you will get a ton of compilation errors:


In the line '"You find yourself outside a small [...] e heat you've managed to conserve."'  , I was expecting that 'building' would be something to 'say', but it didn't look like any form of 'say' that I know. So I tried to read 'building' as a value of some kind (because it's legal to say values), but couldn't make sense of it that way either.

 

That's because I put brackets around the things I don't want to forget to describe.  This technique is called "BENT" or "Bracket Every Notable Thing" which I learned from Aaron A. Reed's book Creating Interactive Fiction with Inform 7.

So lets go back and describe the things.

The building and the wind are scenery in Exterior.

The description of the building is "The building is merely lumps of logs lashed together.  Small but serviceable."

The description of the wind is "A bitter cold wind that would gladly take you away."

The window is scenery in Interior. "Frost covers the glass, preventing you from looking outside."

The fireplace is thing in the Interior. It is an open fixed in place container. "An open fireplace with layers of soot showing that there have indeed been fires inside of it."

The table is a thing in the Interior.  The table is a  supporter. The description of the table is "A lonely wooden table with the dings and scratches of normal household use."

The chair is scenery in Interior. "A rickety old chair that doesn’t look like it could support anyone."

Your fire is scenery. Instead of pushing, pulling, turning, tasting, or touching your fire, say "You would burn yourself." The description of your fire is "A reassuring protection against the cold."

That’s a pretty good start.

The next thing I did was teach Inform about burning and matches.  I didn’t write this code my self.  I adapted it from one of the examples that comes with Inform 7.  ’m not going to include the code here.   If you want to see it, go to the release page: http://www.thecatsweb.com/tutorials/LightMyFire/Inform7/

I am going to show some of the changes I made.

Check burning something with something (this is the burn only things in fireplace rule):
    if the noun is not in the fireplace, say "[one of]It occurs to you to put  [the noun] in the fireplace before burning, just for safety's sake. [or]Again, you decide to put [the noun] into the fireplace prior to burning. [or]You try setting [the noun] in the fireplace as usual. [stopping]" instead.

I changed the “You can’t be holding the item” rule to “The item has to be in the fireplace”. Now, instead of letting you burn things anywhere but the fireplace.

Carry out burning something with something (this is the simplistic burning rule):
    say "You succeed in lighting yourself a proper fire.";
    end the story finally.

If you succeed at burning anything, the game ends.  By this time I was really tired I didn’t come up with any colour text for the ending.

After teaching about matches, all that was left was to set up the scenario:

Part 4 - Start   

A log is a flammable thing in the exterior. Understand "wooden" and "wood" as the log.

The matchbox is an open openable container. It contains five s-matches. It is on the table.

Every turn:
    say "[one of]The wind howls and cuts right through you[or]You shiver[or]You can feel the cold draining your life force[or]You are going to die without a fire of some sort[then at random].";

I put the flammable log in the exterior. I put the matchbox pull of matches on the table, then I set up some text to appear every turn to let the player know that they are cold and should build a fire.

I’ll admit that this is a pretty skimpy game.  It took me all of about an hour and a half to write and that was mostly getting the matches to work.

Please let me know:

  • Do you want to see this game made in other engines? i.e. Unity, Ren’py. RPG Creator. etc
  • Do you want a video walkthrough of me making the game?
  • Do you want me to give a better description of how and why to use Inform7

Thanks for now.

Susan

Other articles in this series:
Making a Basic Game with Inform 7
Sample Game with JSGAM
Making a Basic Game in Twine