Doe's Inform 6 Primer



Pencil. Contents this "Lesson:"
  1. True or false, but, thankfully, no essay.
    How Inform routines return true or false.
  2. Big "bad" table.
    The order in which Inform routines are processed.
  3. Stupid Initialise tricks.
    Setting the location, changing the player (adding body parts), starting the player on something, beginning with
    a quote, creating a timed game, and skipping the banner.
First Homework Assignment:
  1. Stupid Initialise Tricks
    First Exercise:   Create a one room game that begins with a famous quote. Second Exercise:   Make the player non-human, maybe with a tail. Optional: Start the player on/in something, restore a saved game at game start, and/or create a timed game.

    First Homework Assignment Solutions

  1. True or false?

    Inform relies very heavily on true or false. False is actually the same as 0, true is the same as 1 (or any number higher than zero). This is like a computer's bit, 0 or 1, off or on.

    Attributes can be true or false. Properties routines, verbsub and other routines can return values of 0 or 1 (or higher). Attributes are fairly simple, so let's examine a routine's return values a bit more.

    Returning True

    When a routine returns a non-zero value it means, "Stop here, this is the end, proceed no further." Processing stops before other routines can be called and the game returns to the prompt, waiting for more player input.

    The most common way a routine can return 1 is by printing message. This relies on the fact that print_ret "string"; (print return) prints the string, provides a carriage return (new line) and returns 1. "String"; is the same as print_ret "string";, so it also provides a carriage return and returns 1.
    Object chair "chair"
         has supporter enterable
         with name "chair",
         before
         [; Take : "It is too big to carted around.";
         ];
    
    If the player attempts to take the chair, "It is too big to be carted around." will be printed and the chair's before routine will return true (1). The action stops and the prompt appears again.

    Returning False

    Object chair "chair"
         has supporter enterable
         with name "chair",
         before
         [; Take : print "It's pretty heavy. ";
         ];
    
    But print "string"; just prints the string and returns 0. So this prints, "It's pretty heavy. " and returns 0. Because processing was not stopped, Inform next calls its built-in library routine, Take, which checks to see if the chair is touchable, if the player is carrying too much already, etc. If all conditions are right, it moves the chair to the player (to the player's inventory) and print_rets, "Taken", returning 1, thus finally stopping further processing. Put together, this is will show on the screen as -- "It's pretty heavy. Taken."

    Note that print_ret or print, not just string, must be used if you want to call an additional specialized print routine, such as (the) or (name) from within the print statement.

    When a routine returns 0, Inform's built-in routines take over next, until they, in turn, stop (usually by using "string";). So a game author's coding is essentially all exception handling -- interrupting or stopping Inform's default processing.

    A Doe Crib Note:
    Knowing what is/returns true or false -- 1 or 0 -- is essential for understanding Inform.

    Command
    ];
    (end of routine, which returns automatically -- the value
    returned depends on the contents, usually 1, but some
    routines have a default value)
    print "string";
    (prints string and returns 0)
    print_ret "string";
    (prints string with a carriage return (new line) and
    returns 1)
    "string";
    (prints string with a carriage return (new line) and
    returns 1 -- same as above)
    return;
    (returns from a routine with no value, which is
    sometimes handy)
    return #;
    (returns a specific numeric value)
    rfalse;
    (returns false -- 0)
    rtrue;
    (returns true -- 1)


    Contents


  2. Big "Bad" Table

    This comes straight from the Designer's Manual (DM 3), pages:   94, 115, 122, 124, 137, 148 & 163. I have just put it all into one table. You might look this over, but it is really for reference after you already know a little Inform.

    Processing Actions Performed
    start of each turn Look or New Room
        PrintorRun room's initial
        NewRoom
        MoveFloatingObjects

    Look Steps:
    1a. A new-line is printed. The location's short name is printed (in bold-face, if possible).
    1b. If the player is on a supporter, then " (on <something>)" is printed; if inside anything else, then " (in <something>)".
    1c. " (as )" is printed if this was requested by the game's most recent call to ChangePlayer (for instance, " (as a werewolf)").
    1d. A new-line is printed.

    Now the `long description'. This step is skipped if the player has just moved of his own will into a location already visited, unless the game is in "verbose" mode.
    2. If the location has a describe property, then it is run. If not, the location's description property is looked at: if it's a string, it is printed; if it's a routine, it is run.

    All rooms must provide either a describe property or a description of themselves. Now for items nearby:
    3a. Any objects on the floor are listed.
    3b. If the player is in or on something, the other objects also on/in the supporter/container are listed. The library has now finished, but your game gets a chance to add a postscript.

    4. The entry point LookRoutine is called.
    Parsing
    Parses the player's input (breaks sentence down into understandable parts).
    BeforeParsing
    ChooseObjects
    parse_name
    ParseNoun
    animate/talkable's grammar routine
    Before
    An opportunity for your code to interfere with or block altogether what might soon happen.
    GamePreRoutine
    player's orders' property
    react_before of all objects in scope
    before of current room
    before (action) of first noun
    During
    The library takes control and decides if the action makes sense according to its normal world model: for example, only an edible object may be eaten; only an object in the player's possession can be thrown at somebody, and so on. If the action is impossible, a complaint is printed and that's all. Otherwise the action is now carried out.
    LibraryMessages
    After
    An opportunity for your code to react to what has happened, after it has happened but before any text announcing it has been printed. If it chooses, your code can print and cause an entirely different outcome. If your code doesn't interfere, the library reports back to the player (with such choice phrases as "Dropped.").
    react_after of all objects in scope
    after of current room
    after of first noun
    GamePostRoutine
    end of each turn 1.The turns counter is incremented.
    2.The 24-clock is moved on.
    3.Daemons and timers are run (in no guaranteed order).
    4. each_turn takes place for the current room, and then for everything nearby (that is, in
    scope).
    5. The game's global TimePasses routine is called.
    6. Light is re-considered (it may have changed as a result of events since this time last turn).


    Contents


  3. Stupid Initialise Tricks

    Initialise prints any introduction that appears before the game banner and also sets the starting location for the player and sets their starting inventory. Every game must have an Initialise routine, and after the game is loaded, it is called first -- ergo, its name. (But note the lofty British spelling -- that has tripped me up more than once ;-).)

       [ Initialise;
         location = field;
         move lantern to player.
         "You don't know how you got here. You were just strolling
          along minding your own business, when this big troll...";
       ];
    
    Of course, you put the printed string last because that will return true (1).

    Changing the Player

    That's pretty simple. But suppose you want to change the player? Say you want a customized player with body parts.

    Copy selfobj in parserm.h to your code, name the copy something like newselfobj, customize the player how you want, then in Initialise put...

    player = newselfobj;
    
    A Doe Crib Note:
    When changing the player in Initialise use the equal sign. When changing the player elsewhere in your game, use ChangePlayer(newselfobj);. Why? Well, ChangePlayer() performs all kinds of complicated maneuvers (MoveFloatingObjects(), OffersLight(), etc.) that Initialise also performs. So if you call ChangePlayer in Initialise, they will be done twice. Also Zarf recommends = in Initialise. Good enough for me ;-).

    However, if you want the game to report the player's changed state after the room name, (as a dog), then you must use ChangePlayer and use it with a 1 -- ChangePlayer(dog, 1);

    As far as how to add body parts, use add_to_scope hands or put Object -> hands "hands" (or whatever body part you are adding) directly under the newselfobj.

    Object selfobj "(self object)"       <- original player object from parserm.h
      with short_name
           [;  return L__M(##Miscellany, 18);
           ],
           description
           [;  return L__M(##Miscellany, 19);
           ],
           before NULL,   after NULL,    life NULL,    each_turn NULL,
           time_out NULL, describe NULL,
           capacity 100, parse_name 0,
           orders 0, number 0,
      has  concealed animate proper transparent;
    
    
    Object newselfobj "(self object)"    <- new player object
    
      has concealed animate proper transparent
      with short_name
           [;  return L__M(##Miscellany, 18);
           ],
           description
           [;  return L__M(##Miscellany, 19);
           ],
           before NULL,   after NULL,    life NULL,    each_turn NULL,
           time_out NULL, describe NULL,
           capacity 100, parse_name 0,
           orders 0, number 0,
     add_to_scope hands;
    
    Object hands "hands"
     has concealed static transparent
     with name "hands", article "your",
     description
     [; if (gloves has worn)
           "On your hands are a pair of gloves.";
        "Your hands are bare.";
     ],
     add_to_scope l_hand r_hand;
    
    Object l_hand "left hand"
     with name "left" "hand";
    
    Object r_hand "right hand"
     with name "right" "hand";
    

    Starting the Player on/in Something

    Another tricky thing is having a game start with the player SITTING on something. Use...
    location = office;
    location = chair;
    
    ..instead of...
    location = office;
    move player to chair;
    
    A Doe Crib Note:
    I have never had a satisfactory explanation of why the first works and the second doesn't. But to start the player on or in an object, use location. It's also a good idea to set the player's location and/or change the player before doing anything else.

    How about a quote box at the beginning of your game? Or having it start by asking if you want to restore a saved game? Or skipping the banner until later? Or having a timed game? These are all things that are done in Initialise as well.

    1. Quote Box

      Before the ; at the start of any routine (Initialise, before, after) is where you can put local variables (discardable variables that start at zero each time that particular routine is called). Also, Inform has a nice built-in quote box routine.

      [ Initialise width length i;  ! <- local variables
        location = field;
        move lantern to player;
        box ""
            "There is nothing quite so silly as a bad writer writing a tutorial."
            ""
            "                      Marnie Parker"
            "";
        width = 0->33;
        if (width == 0) width = 80;
        if (width > 25) length = (width - 25)/2;
        else length = 1;
        style bold;
        spaces length;
        print "[Press any key to begin.]";
        style roman;
        @read_char 1 i;
        @erase_window $ffff;
         "^^^You don't know how you got here. You were just strolling
         along minding your own business, when this big troll..."
      ];
      
      width = 0->33; gets the default screen size (in most cases). The rest of that calculation is used to center the prompt, "[Press any key to begin.]" (25 characters). @read_char waits for the player to press a key (any key), and @erase_window clears the screen after the quote box is printed and before the introduction is printed. ^ prints a carriage return without returning 1. It is used here because after clearing the screen the opening sentence may be lost without some preceeding carriage returns.

    2. Restoring a Saved Game

      Something similar can be done to ask the player if they want to restore a saved game, using the built-in Inform routine, YesorNo.
      print "Do you want to restore a saved game? (Y/N) ";
      if (YesorNo() ~= 0) RestoreSub(); 
      
    3. Skipping the Banner

      The banner displays the name of the game (Constant Story - optional), the name of the author (Constant Headline - optional), the Inform compiler version, and the Inform library version (both built-in). Normally it appears at game start. To skip the banner, initialise must return 2 instead of the 1 it usually returns. Be sure to use print "string" instead of "string" so Initialise doesn't inadvertently return 1 before you get to the return 2. Then the banner can be called later in your game with Banner();

      [ Initialise;
        location = field;
        move lantern to player.
        print "You don't know how you got here. You were just strolling
               along minding your own business, when this big troll...^^";
        return 2;
      ];
      
      Note that the banner cannot be permanently skipped. One of Dr. Nelson's conditions granting you the use of Inform, for free, is that you must include the banner near the start of the game, because it lists the Inform compiler and Inform library versions.

    4. Creating a Timed Game

      A timed game usually has the time (elapsed) on the status line instead of how many turns have elapsed. To make sure the status line shows the time instead, include

      Statusline time; 
      
      before Initialise and after including parser.h. Making a timed game actually work also requires using default Inform routine that should be set in Initialise:
      SetTime(hours, rateperturn);
      
      Hours is the current time, ratepersturn is how many minutes you want to allot to each turn. The only thing tricky about that is using a 24-hour clock.


    Actually, no Initialise tricks are tricky once you know how.


Previous Page - Contents & Section 1    Next Page - Sections 5 & 6 and Second Homework Assignment

Top of Page    Infotips    Interactive Fiction Main Page

If this primer has been of any use to you I wouldn't mind hearing about it.