! ----------------------------------------------------------------------------
!  Toyshop:  An Inform Demonstration                       Version 3, 21/12/94
!
!  This is not a real game.  The main example game for Inform is "Advent",
!  a port of Colossal Cave.  Since that's something of an antique, and most
!  of the objects in it are rather simple, this is a collection of more
!  exotic features and peculiar objects.  Note that "Advent" has plenty of
!  interesting doors and also provides a good lantern and bottled water.
!
!  Needs Inform 5.5 with library 5/12 or later to compile.
!
!  To win, simply find 6 interesting things to do and leave by the main exit!
!
!       Object            Is an example of...
!
!  >SA  satchel           Container into which the game silently puts things
!  >HE  helium balloon    Something moving under the control of a daemon
!  >CA  little red car    Vehicle, and pushable from place to place
!  >PF  padded floor      Scenery present in several rooms at once
!  >GR  hand grenade      Timed events: a grenade and its pin
!  >MA  matchbook         Simple fire and matches; changing inventory styles
!  >WC  white candles     A stock of objects identical to each other
!  >GL  white gloves      Two independent objects which can behave as a pair
!  >CO  green cone        Easy before and after rules
!  >HW  high window       Starting and stopping daemons
!  >BC  bolted cupboard   A typical locked container (with key)
!  >GB  glass box         Container light can get through
!  >SB  steel box         Container light can't get through
!  >CU  building cubes    A complicated class definition; piles of objects
!  >CH  Christopher       Someone you can talk to, and persuade to do things
!  >OF  Office            Rules about moving in a particular direction
!  >TB  toothed bag       A container with ideas about what it will allow
!  >SL  spirit level      Something to put on top of things
!  >BB  blackboard        A blackboard to write messages on
!
!  (The code is marked with >SA and so on for easy access with a text editor)
! ----------------------------------------------------------------------------

Switches dv5;

!   First, the banner:

Constant Story "TOYSHOP";
Constant Headline "^An Interactive Demonstration^\
            Copyright (c) 1994 by Graham Nelson. All rights given away.^";
Release 3;
Serial "951221";   !   This sets the serial date to the date of this source
                  !   file, not to the date of compilation.

!   Now we serve notice to Inform that we do not wish to use the standard
!   routine for the Burn action, and will instead be defining our own:

Replace BurnSub;

!   And include the first of the three standard library files:

Include "Parser";

! ----------------------------------------------------------------------------
! >SA  Ungenerously, the player can only carry at most 4 things, but there's
!      a satchel to carry other things around in...
! ----------------------------------------------------------------------------

Object satchel "satchel"
 with description "Big and with a smile painted on it.",
      name "satchel", article "your",
      when_closed "Your satchel lies on the floor.",
      when_open "Your satchel lies open on the floor.",
 has  container open openable;

Constant MAX_CARRIED 4;
Constant SACK_OBJECT satchel;

!   We're going to use the full, most elaborate scoring system the
!   library provides, so we define all this...

Constant TASKS_PROVIDED;
Constant NUMBER_TASKS 6;
Array    task_scores  -> 1 1 1 1 1 1;
Constant MAX_SCORE 6;

!   Finally, include the library of standard verbs and actions...

Include "VerbLib";


! ----------------------------------------------------------------------------
!   Off we go into the Toyshop...
! ----------------------------------------------------------------------------

Object Toyshop "Toyshop"
 with description
         "The centre of a long east-west hall.  Shelves are lined \
          with toys, painted clowns face you from the walls and \
          the floor is lightly padded with colourful mats.  A doorway \
          leads north, with a red warning triangle above it.",
      name "clowns" "painted" "shelves" "triangle",
      e_to East_End, w_to West_End, n_to Danger_Zone
 has  light;

! ----------------------------------------------------------------------------
! >HE  The balloon is completely self-contained as a piece of code, except
!      that it does not set itself going (though this could have been
!      arranged): it is set going in the Initialise() routine.
!
!   Notice that the "after" for Drop takes away the "moved" attribute.
!   This is a device to ensure that the "initial" message will always be
!   the one displayed.  (There are other ways of doing this, too.)
! ----------------------------------------------------------------------------

Nearby balloon "helium balloon"
 with description "Blue, with a yellow smile.",
      name "helium" "balloon" "blue" "string",
      initial "A balloon nestles on the ceiling, its long string hanging.",
      before
      [; Attack: remove self; StopDaemon(self);
               "Easily, you burst the balloon.  Pop!^^\
                Shame it was irreplaceable, really.";
      ],
      after
      [; Take: "You take the balloon by its string.  It's buoyant!";
         Drop: give balloon ~moved;
               "The balloon rises gracefully to the ceiling.";
      ],
      time_left 0,
      daemon
      [ from_room to_room;
         if (random(3)~=1) rfalse;
         from_room=parent(self);
         if (from_room==East_End or West_End) to_room=Toyshop;
         if (from_room==Toyshop)
         {   if (random(2)==1) to_room=East_End;
             else to_room=West_End;
         }
         if (to_room==0) rfalse;
         move self to to_room;
         if (location==from_room)
             print_ret "^A breeze blows the balloon away to the ",
                       (name) to_room, ".";
         if (location==to_room)
             print_ret "^A breeze blows the balloon in from the ",
                       (name) from_room, ".";
      ],
 has  light;    ! Dismal, dismal pun

! ----------------------------------------------------------------------------
! >CA  There are two exceptions to the ordinary before/after rules, for
!      vehicles and things which can be pushed from place to place: this car
!      demonstrates both at once.
!
!   The "before" for PushDir (push in a named direction) must call
!   AllowPushDir and then return true to signify that the push is legal.
!
!   The "before" for Go must return true to signify that travelling in
!   the object is legal.  (Note that it must also be enterable.)
! ----------------------------------------------------------------------------

Nearby car "little red car"
 with name "little" "red" "car" "kar1",
      description "Large enough to sit inside.  Among the controls is a \
                prominent on/off switch.  The numberplate is KAR 1.",
      when_on  "The red car sits here, its engine still running.",
      when_off "A little red car is parked here.",
      before
      [; PushDir: AllowPushDir(); rtrue;
         Go: if (car has on) { Achieved(1); "Brmm!  Brmm!"; }
             print "(The ignition is off at the moment.)^";
      ],
      after
      [; PushDir: "The car rolls very slowly as you push it.";
      ],
 has  switchable enterable static container open;

Object small_note "small note" car
 with name "small" "note",
      description
          "  !!!! FROBOZZ MAGIC CAR COMPANY !!!!^\
          ^Hello, Driver!^\
          ^Instructions for use:^\
          ^Switch on the ignition and off you go!^\
          ^Warranty:^\
          ^This car is guaranteed against all defects for a period of \
           76 milliseconds from date of purchase or until used, \
           whichever comes first.^\
          ^Good Luck!";

! ----------------------------------------------------------------------------
! >PF  An example of an object spread across several (three) rooms:
! ----------------------------------------------------------------------------

Object padded_floor "padded floor"
 with name "padded" "floor" "mats" "padding",
      description "To protect little children and adventurers.",
      before
      [; Take: "It is protected from little children and adventurers.";
      ],
      found_in East_End Toyshop West_End
 has  scenery;

! ----------------------------------------------------------------------------

Object Danger_Zone "Danger Zone"
 with description
         "This is the Danger Zone, which you should know better \
          than to go into.  A single door leads back south.",
      s_to Toyshop
 has  light;

! ----------------------------------------------------------------------------
! >GR  A classic example of a timer (or, as some people call them and
!      appropriately so in this case, a fuse).  To demonstrate stopping
!      a timer before the alarm (and for fun), there is also a pin:
! ----------------------------------------------------------------------------

Nearby grenade "nasty-looking hand grenade"
 with name "hand" "grenade" "nasty" "nasty-looking",
      initial "A nasty-looking hand grenade (there is no other kind) \
               rolls about irresponsibly on the floor.",
      description "Not recommended for children under 90.",
      before
      [; Pull: if (self has general) "Too late for that.";
             StartTimer(self, 5); give self general;
             move the_pin to player;
             "You pull the pin out, an irrevocable act.";
      ],
      time_left 0,
      time_out
      [;  deadflag=1;
          "^An immense explosion suddenly demolishes the toyshop!^^\
            Will you never learn?";
      ],
 has  transparent;

Object the_pin "pin" grenade
 with name "pin",
      description "The pin is designed to be easy to pull.",
      before
      [; Take, Pull: if (self in grenade) <<Pull grenade>>;
         Insert:
             if (self notin grenade && second==grenade)
             {   StopTimer(grenade); move self to grenade;
                 give grenade ~general;
                 "Amazing!  You got the pin back into the grenade!";
             }
      ];

! ----------------------------------------------------------------------------
! >MA  This is a matchbook of five matches, which is quite simple in that you
!      can only actually have one match at a time: otherwise, it's quite
!      a full implementation.  Note that the inventory lines for the match
!      and the matchbook are coded here.  Note also that the "match" object
!      returns to the book even when the book is empty, so that the parser
!      will still understand requests for matches - which the "before" rule,
!      which automatically removes matches when needed, can then turn down.
!
!      The matchbook has a daemon whose job is to tidy up lost matches.  One
!      might expect this rule to be coded with an "after" routine, to trap
!      the player dropping matches.  But suppose there were a magpie in the
!      game, and it flew down and stole the match but left the matchbook!
!      As it happens there isn't, but this is better form.
! ----------------------------------------------------------------------------

Object matchbook "matchbook" Danger_Zone
 with name "matchbook" "book" "matches",
      number 5,
      before
      [; Burn: if (match has light)
               {   remove match; remove matchbook;
                   "What a waste of matches!";
               }
      ],
      invent
      [;  if (inventory_stage==2)
          {   switch(self.number)
              {   0: print " (empty)";
                  1: print " (1 match left)";
                  default: print " (", self.number, " matches left)";
              }
          }
      ],
      description
      [;  print "The cover advertisement reads \
                 ~Curses - Adventure of a Lunchtime~.  The book ";
          switch(self.number)
          {   0: "is empty.";
              1: "has a single match left.";
              default:
                  print_ret "contains ", self.number, " matches.";
          }
      ],
      time_left 0,
      daemon
      [;   if (match notin matchbook && match notin player)
           {   move match to matchbook;
               if (match has light)
               {   give match ~light; StopTimer(match); }
               StopDaemon(self);
           }
      ],
 has  transparent;

Object match "match" matchbook
 with parse_name
      [ i j;   if (self has light) j='burning'; else j='unlit';
               while (NextWord()=='match' or j) i++;
               return i;
      ],
      time_left 0, article "an",
      before
      [ i; if (self in matchbook)
           {   i=matchbook.number;
               if (i==0) "There are no matches left in the book.";
               i--; matchbook.number=i;
               move self to player; StartDaemon(matchbook);
               print "(taking a match from the book, which ";
               if (i==0) print "is now empty)^";
               if (i==1) print "has one more left)^";
               if (i>1)  print "has ", i, " left)^";
               self.article = "an";
           }
           Take, Remove: if (self in player) "Done.";
           Burn:
               if (self has light) "The match is already alight.";
               if (matchbook notin player)
                  "You need the matchbook to strike the match.";
               give self light; StartTimer(self, 2+random(3));
               self.article = "a";
               "You strike the match.";
      ],
      short_name
      [;   if (self has light) print "burning match";
                          else print "unlit match";
           rtrue;
      ],
      time_out
      [;   move self to matchbook; give self ~light;
           "^You drop the match as the flame reaches your finger.";
      ];

! ----------------------------------------------------------------------------
! >WC  And now, a proper solution to the "matchbook problem": a box of candles
!      where you really can take as many as you like!
!      (Exercise: make the candles lightable.  You'll find the game then
!      divides candles into lit and unlit ones automatically.)
!
!      Actually, there is a more sophisticated way of defining such objects.
!      This is almost perfect, except that "take candles" will not pick up
!      all the candles in sight (though "take four candles", etc., will work).
!      For the deluxe way to code this, see the "Balances" example game.
! ----------------------------------------------------------------------------

Class  candle_class
 with name "wax" "candle" "candles",
      short_name "wax candle", plural "wax candles",
      description "It looks just like all the other candles.",
      before
      [; Burn: "Disappointingly, the wick refuses to burn."; ];

Object candle_box "grey tin box" Danger_Zone
 with name "tin" "box" "grey",
      description
          "A grey tin box of ~Major's Candles~.",
 has  container openable;
Nearby c1 "c" class candle_class;
Nearby c2 "c" class candle_class;
Nearby c3 "c" class candle_class;
Nearby c4 "c" class candle_class;
Nearby c5 "c" class candle_class;
Nearby c6 "c" class candle_class;
Nearby c7 "c" class candle_class;
Nearby c8 "c" class candle_class;

! ----------------------------------------------------------------------------

Object East_End "East End"
 with name "dolls" "nurses",
      description
         "The eastern end of the toyshop is pink, and dolls and \
          nurses line the shelves right up to the high window.  \
          A dark doorway leads to a northern side chamber.",
      w_to Toyshop, n_to DarkRoom
 has  light;


! ----------------------------------------------------------------------------
! >GL  The following example, suggested to the author by Richard Tucker,
!      demonstrates an apparently tricky case of objects with associated
!   sub-objects.  The pair of white gloves behaves just like any other item
!   of clothing - but the player can also use the left and right gloves
!   independently, can take away or wear only one and so on.  When they
!   come back together (even in a cupboard, say, or on a mantelpiece)
!   they are called a pair again.
!
!   We can do this with only three objects, one daemon and one rule.
!
!   When the gloves are together, and the player refers to an individual
!   glove, the before rule splits up the pair and starts the daemon.
!   Once active, the daemon tries every turn to re-join them into a pair.
!   (If it succeeds, it turns itself off.)
!
!   Note that the "pair of gloves" object has the "general" flag exactly when
!   the gloves are apart.  When they are together, it contains the individual
!   glove objects, and is flagged as "transparent" so that the parser knows
!   the player can see and refer to them.
! ----------------------------------------------------------------------------

Nearby gloves "white gloves"
 with article "a pair of",
      name "white" "gloves" "pair" "of",
      time_left 0,
      daemon
      [;  if (parent(right_glove)~=parent(left_glove)) rfalse;
          if ((left_glove has worn && right_glove hasnt worn)
              || (left_glove hasnt worn && right_glove has worn)) rfalse;
          if (left_glove has worn) give gloves worn; else give gloves ~worn;
          move gloves to parent(right_glove); give gloves ~general;

          move right_glove to gloves; move left_glove to gloves;
          give right_glove ~worn;    give left_glove ~worn;

          StopDaemon(self);
      ],
 has  clothing transparent;

Class  glove_class
 with article "the",
      name "white" "glove",
      before
      [;  if (self notin gloves) rfalse;
          move left_glove to parent(gloves); move right_glove to parent(gloves);
          if (gloves has worn)
          {   give left_glove worn; give right_glove worn;
          }
          give gloves general; remove gloves;
          StartDaemon(gloves);
      ],
 has  clothing;
Object left_glove "left glove" gloves
class glove_class
 with description "White silk, monogrammed with a scarlet R.",
      name "left";
Object right_glove "right glove" gloves
class glove_class,
 with description "White silk, monogrammed with a scarlet T.",
      name "right";

! ----------------------------------------------------------------------------
!   ...and that's all: the "gloves" code is self-contained.
!
!   Exercise for the reader: hide a (sharp) jewel inside the left glove.
!     (Alter the glove class to make them containers open only when not worn.
!      Add two "after" rules to warn the player if there's something sharp
!      to the touch, one for putting on the pair of gloves, one for putting on
!      an individual glove.)
! ----------------------------------------------------------------------------


! ----------------------------------------------------------------------------
! >CO  A traditional Inform example object:
! ----------------------------------------------------------------------------

Object cone "green cone" East_End
 with name "green" "cone" "emerald" "marzipan",
      describe
      [; if (cone has moved)
             "^A misshapen cone of green marzipan sits here.";
         "^Nearby is an emerald green cone, one foot high.";
      ],
      description "The cone seems to be made of emerald-coloured \
                   marzipan.",
      before
      [; Eat: if (random(100) <= 30)
              {   deadflag = 1;
                  "Unfortunately, you seem to be allergic to almonds.";
              }
              "You nibble at a corner of the cone.";
      ],
      after
      [; Take: "Taken.  (Your hands are smeared with marzipan.)";
         Drop: cone.description = "The cone is a vague green mess.";
               "The cone drops to the floor and sags a little.";
      ],
 has  edible;

! ----------------------------------------------------------------------------
! >HW  It's the draught from this slightly-concealed window which propels the
!      balloon:
! ----------------------------------------------------------------------------

Object high_window "high window" East_End
 with name "high" "window",
      description
      [;  print "A narrow, high window ";
          if (self has open) "through which a draught blows.";
          "which is closed.";
      ],
      after
      [; Open: StartDaemon(balloon);
         Close: Achieved(2); StopDaemon(balloon);
      ],
 has  scenery openable open;

! ----------------------------------------------------------------------------
! >BC  A typical locked container, containing a rather pathetic prize...
! ----------------------------------------------------------------------------

Object cupboard "bolted cupboard" East_End
 with name "bolted" "cupboard",
      describe
      [; if (self hasnt open) "^A shut cupboard is bolted to one wall.";
         "^Bolted up on one wall is an open cupboard.";
      ],
      with_key key
 has  locked container openable lockable static;

Object boiled_sweet "boiled sweet" cupboard
 with name "boiled" "sweet",
      after
      [; Eat: Achieved(0);
              "It takes an irritatingly long time to eat.";
      ],
 has  edible;

! ----------------------------------------------------------------------------
! >GB  This is really to demonstrate "transparent".  Shutting up the glowing
! >SB  ball in the glass box does not make the room go dark: shutting it up
!      in the steel box does.  Also, you can examine things in the glass box
!   even when the glass box is shut.
! ----------------------------------------------------------------------------

Object DarkRoom "Dark Room"
 with description "A featureless storage room, hardly worth illumination.",
      cant_go "The only exit is back south.",
      s_to East_End;

Nearby glass_box "glass box with a lid"
 with name "glass" "box" "with" "lid"
 has  container transparent openable open;

Nearby steel_box "steel box with a lid"
 with name "steel" "box" "with" "lid"
 has  container openable open;


Object West_End "West End"
 with name "soldiers" "model" "aircraft" "planes",
      description
         "The western end of the toyshop is blue, and soldiers and \
          model aircraft line the shelves.  A small office lies to \
          the south.",
      e_to Toyshop, s_to Office
 has  light;

! ----------------------------------------------------------------------------
! >CU  An interesting class definition.  Imagine trying to code this up
!      without classes...  (The following is not for the fainthearted.)
!
!   Note that with the "describe" routine missing, the game would still
!   correctly describe stacks of cubes: just a little less elegantly.
! ----------------------------------------------------------------------------

Attribute is_cube;

Class  Cube_Class
 with description "Just a child's building block, four inches on a side.",
      parse_name
      [ i j;
        for (::)
        {   j=NextWord();
            if (j=='block' or 'cube' or 'building' || j==self.name) i++;
            else
            {   if (j=='blocks' or 'cubes')
                {   parser_action=##PluralFound; i++; }
                else return i;
            }
        }
      ],
      describe
      [ c d e;
          d=child(self);
          while (d~=0 && d has is_cube)
          {   c++; e=d; d=child(d); }
          if (c==0) rfalse;
          print "^There is a pile of building blocks here, ";
          while (c>=0)
          {   print (address) e.name;  ! Sneaky: print the "name" out
              if (c>0) print " on ";   ! using its dictionary address
              c--; e=parent(e);
          }
          ".";
      ],
      before
      [ c;
        PutOn:
          if (second has is_cube)
          {   if (child(second)~=0 && child(second) has is_cube)
                  "There's no room on the top of one cube for two more, side \
                   by side.";
          }
          else
              print "(They're really intended \
                     to be piled on top of each other.)^";
          c=second; while (c has is_cube) c=parent(c);
          if (c~=location or mantelpiece) "Too unsteady a base.";
      ],
      after
      [ c stack;
        PutOn:
          stack=noun;
          while (parent(stack) has is_cube) { stack=parent(stack); c++; }
          if (c<2)
          {   if (Chris has general) rtrue;
              rfalse;
          }
          if (c==2) "The pile of three cubes is unsteady, but viable.";
          if (Chris has general)
          {   Achieved(3);
              "^Expertly he keeps the pile of four cubes stable.";
          }
          stack=noun;
          while (parent(stack) has is_cube)
          {   c=stack; stack=parent(stack); move c to location; }
          "The pile of four cubes wobbles, wobbles, steadies... and suddenly \
           collapses!";
        Take:
          stack=child(noun); if (stack==0) rfalse;
          while (stack~=0)
          { c=stack; stack=child(stack); move c to location; }
          "Your pile of cubes is collapsed as a result.";
      ],
 has  supporter is_cube;

Nearby cube1 "green cube"
class Cube_Class
 with name "green";
Nearby cube2 "red cube"
class Cube_Class
 with name "red";
Nearby cube3 "yellow cube"
class Cube_Class
 with name "yellow";
Nearby cube4 "blue cube"
class Cube_Class
 with name "blue";

! ----------------------------------------------------------------------------
! >CH  A guest appearance by my cousin Christopher, aged six (*), who plays
!      with one thing at a time (easily forgetting which). Being "transparent"
!      (no reflection on him!) means the parser allows the player to examine
!      whatever he's playing with... but not to take it from him.
!      (* In 1993, when this game was first written.)
! ----------------------------------------------------------------------------

Nearby Chris "Christopher"
 with name "child" "boy" "chris" "christopher",
      describe
      [;  print "^A boy called Christopher sits here";
          if (child(Chris)~=0)
              print ", playing with ", (a) child(Chris);
          ".";
      ],
      life
      [ x;
          Ask:
             switch(second)
             {   'juggling', 'fluorescent', 'ball': "~That's mine!~";
                 'helium', 'balloon': "Christopher yawns.";
                 'cube', 'cubes': "~Bet I can make a higher tower than you.~";
                 'toys', 'toyshop': "~Isn't it fabulous here?~";
                 default: "~Dunno.~";
             }
          Answer:
             switch(noun)
             {   'hello', 'hallo', 'hi':
                      "~Hello,~ says Christopher cheerfully.";
                 default: "Christopher seems preoccupied.";
             }
          Attack: remove self;
            "Christopher makes a run for it, effortlessly slipping past you!";
          Kiss: "~That's soppy, that is.~";
          Give:
            if (noun==balloon) "He's too bored by the balloon.";
            x=child(Chris);
            if (x~=0)
            {   move x to location;
                print "He forgets about ", (the) x, " and ";
            }
            else print "He ";
            print "eagerly grabs ", (the) noun; move noun to Chris; ".";
      ],
      orders
      [;  Drop: if (noun in Chris) "~Won't!  It's mine!~";
          Take: "Christopher can't be bothered.";
          Give: if (second==player) "~Get your own!~";
          Go: "~But I like it here!~";
          PutOn: if (noun notin Chris) "He is mightily confused.";
                if (noun hasnt is_cube || second hasnt is_cube)
                    "He can't see the point of this.";
                print "Christopher leans over with great concentration \
                    and does so.^";
                move noun to player; give self general;
                <PutOn noun second>;
                give self ~general; rtrue;
      ],
      each_turn
      [;  if (random(3)~=1) rtrue;
          print "^Christopher ";
          switch(random(4))
          {  1: "yawns.";     2: "frowns.";
             3: "stretches."; 4: "hums tonelessly.";
          }
      ],
 has  animate proper transparent;

Object ball "fluorescent juggling ball" Chris
 with initial "On the floor is a fluorescent juggling ball!",
      name "fluorescent" "juggling" "ball",
      description "It glows with soft light."
 has  light;

! ----------------------------------------------------------------------------
! >OF  A simple movement rule.
! ----------------------------------------------------------------------------

Object Office "Office"
 with description
         "A small, grey office, with a broad stone mantelpiece.  \
          In the east wall is a doorway marked ~Exit~, and the Toyshop, \
          of course, lies north.",
      cant_go "The Toyshop floor lies north.",
      n_to West_End,
      e_to
      [; if (score~=MAX_SCORE)
             print_ret "A gong sounds.  ~You cannot leave the Toyshop until \
                 you have done six interesting things!~";
         deadflag=2;
         "A gong sounds.  ~Congratulations!  You may now leave the Toyshop \
          and begin writing your own Inform game!~";
      ],
 has  light;

Nearby mantelpiece "mantelpiece"
 with name "mantel" "mantle" "piece" "mantelpiece"
 has  scenery supporter;

! ----------------------------------------------------------------------------
! >TB  A somewhat acquisitive container... but it can be taught to behave.
! ----------------------------------------------------------------------------

Nearby toothed_bag "toothed bag"
 with name "toothed" "bag",
      initial "In one corner is a curious, toothed bag.",
      description "A capacious bag with a toothed mouth.",
      before
      [; LetGo: "The bag defiantly bites itself \
                 shut on your hand until you desist.";
      ],
      after
      [; Receive:
             if (noun==cone)
             {   self.before=0; self.after=0;
                 "The bag wriggles interminably as it tries \
                  to eat the enormous mass of marzipan.  That'll \
                  teach it.";
             }
             print_ret "The bag wriggles hideously as it swallows ",
                 (the) noun, ".";
      ],
 has  container open;

! ----------------------------------------------------------------------------
! >SL  Which can be put on the mantelpiece: the first time this is done, the
!      game randomly decides which end is higher, and sticks to this decision.
! ----------------------------------------------------------------------------

Object spirit_level "spirit level" toothed_bag
 with name "spirit" "level" "wood" "flask",
      number 0,
      description "A length of wood containing a flask of viscous \
          green liquid, in which a bubble is trapped.",
      before
      [; Examine:
         if (parent(spirit_level)==mantelpiece)
         {   print "The bubble is at the ";
             if (self.number==1) "northeast end.";
             "southeast end.";
         }
      ],
      after
      [; PutOn: if (second~=mantelpiece) rfalse;
          if (spirit_level hasnt general) self.number=random(2);
          give spirit_level general; Achieved(4);
          print "You put the spirit level on the mantelpiece, \
                 and the bubble slowly drifts towards the ";
          if (self.number==1) "northeast.";
          "southwest.";
      ];

Object key "iron key" mantelpiece
 with name "iron" "key", article "an";

! ----------------------------------------------------------------------------
! >BB  A blackboard which can be written on or wiped clear.
! ----------------------------------------------------------------------------

Object chalk "stick of chalk" mantelpiece
 with name "stick" "of" "chalk";

Global boardtext string 64;

Object blackboard "blackboard" Office
 with name "board" "blackboard" "black",
      describe
      [;  <<Examine self>>; ],
      before
      [ i f;
          Examine:
              for (i=1:i<=boardtext->0:i++)
                  if (boardtext->i~=' ' or 0) f=1;
              if (f==0)
              {   print "^The office blackboard is wiped clean.^";
                  if (self hasnt general)
                  {   give self general;
                      "^[To write on it, try   > write ~message...~]";
                  }
                  rtrue;
              }
              print "^The office blackboard bears the message:^    ";
              for (i=1:i<=boardtext->0:i++)
              {   f=boardtext->i;
                  if (f~=0) print (char) f;
              }
              new_line; rtrue;
          Rub: for (i=1:i<=boardtext->0:i++) boardtext->i = ' ';
                 "You wipe the blackboard clean.";
      ],
 has  static;

Global from_char; Global to_char;
[ QuotedText i j f;
  i = WordAddress(wn++); i=i-buffer;
  if (buffer->i=='"')
  {   for (j=i+1:j<=(buffer->1)+1:j++)
          if (buffer->j=='"') f=j;
      if (f==0) return -1;
      from_char = i+1; to_char=f-1;
      if (from_char>to_char) return -1;
      while (buffer+f > WordAddress(wn)) wn++; wn++;
      return 1;
  }
  return -1;
];

[ WriteSub i j;
  if (chalk notin player) "You're holding nothing to write with.";
  if (blackboard notin location) "The blackboard is elsewhere.";
  for (i=from_char,j=1:i<=to_char && j<boardtext->0:i++,j++)
      boardtext->j = buffer->i;
  for (:j<boardtext->0:j++) boardtext->j=0;
  Achieved(5);
  <<Examine blackboard>>;
];

! ----------------------------------------------------------------------------
!   End of object definitions.
! ----------------------------------------------------------------------------
!
!   Routines and Entry Points
!
!   (Fuller examples can be found in the "Advent" source code.)
!
!   Initialise() just sets up the initial state of the game.
!   We are required to set "location" to the start location of the
!   player; the rest is optional.
!
!   StartDaemon(balloon)  starts the process which blows the balloon back
!   and forth.
! ----------------------------------------------------------------------------

Object chair "high chair" Toyshop
 with name "chair" "high"
 has  supporter enterable;

[ Initialise;
 location=chair;  move satchel to player;

 print "^^^^^~What's so special about Inform,~ is the last thing you \
        remember saying to the mad alchemist.  Big mistake...^^";

 StartDaemon(balloon);
];

! ----------------------------------------------------------------------------
!   Print names of tasks out (when the library asks us to).  Note that they
!   are numbered from 0 to NUMBER_TASKS-1.
! ----------------------------------------------------------------------------

[ PrintTaskName achievement;
 switch(achievement)
 {   0: "eating a sweet";
     1: "driving the car";
     2: "shutting out the draught";
     3: "building a tower of four";
     4: "seeing which way the mantelpiece leans";
     5: "writing on the blackboard";
 }
];

[ PrintRank;
 print ", earning you the rank of ";
 if (score >= 6)  "Toyshop manager.";
 if (score >= 5)  "management trainee.";
 if (score >= 4)  "undergraduate.";
 if (score >= 3)  "schoolchild.";
 if (score >= 2)  "nursery-school child.";
 if (score >= 1)  "toddler.";
 "newborn baby.";
];

! ----------------------------------------------------------------------------
!   Now (as promised earlier) we provide the replacement for BurnSub,
!   specially adapted to the rules of the Toyshop:
! ----------------------------------------------------------------------------

[ BurnSub;
   if (match hasnt light) "You have no source of flame.";
   if (noun has animate) <<Attack noun>>;
   if (noun==padded_floor)
   {   deadflag=1;
       "A gong sounds, but before a sepulchral voice finishes clearing \
        its throat, the whole padded floor goes up in an inferno.";
   }
   "A gong sounds, and a sepulchral, rather disappointed voice says: \
    ~It is forbidden to play with fire in the Toyshop.~";
];

! ----------------------------------------------------------------------------
!   And we provide one new action, "Burst", which in fact just passes over to
!   "Attack", plus one for writing on the board:
! ----------------------------------------------------------------------------

[ BurstSub; <<Attack noun>>; ];

Include "Grammar";

Verb "burst" "pop" "prick" "stab" "pierce"
               * noun                           -> Burst;

Verb "write"    * QuotedText -> Write;

! ----------------------------------------------------------------------------