Inform - Resources - Examples

Back to List

Inventory
Complete

Plain
Coloured
Gaudy

Browsing Balances.inf

This is the complete source code of the example game Balances.inf.

0001  ! ----------------------------------------------------------------------------
0002  !   Balances 961216                 One of the standard Inform 6 example games
0003  !
0004  !                                                             created: 25.9.94
0005  !                                                             updated: 6.10.94
0006  !                                                         modernised: 11.12.95
0007  !                                               translated to Inform 6: 8.5.96
0008  !                                                   minor bugs fixed: 16.12.96
0009  !
0010  !   This short story was written to demonstrate large-scale programming of
0011  !   the parser, and features multiple objects, complicated plurals, variable
0012  !   verbs, objects named by the player and questions.  The spell-casting
0013  !   system is written in a "safe" way so that it could easily be transplanted.
0014  !
0015  !   Needs Inform 6, library 6/1 or later to compile.
0016  ! ----------------------------------------------------------------------------
0017   
0018  Release 5;
0019  Serial "961216";
0020  Switches d;
0021   
0022  Constant Story "BALANCES";
0023  Constant Headline "^An Interactive Short Story
0024                     ^Copyright (c) 1994, 1995, 1996 by Graham Nelson.^";
0025   
0026  Constant OBJECT_SCORE 5;
0027  Constant MAX_SCORE 51;
0028   
0029  Include "Parser";
0030  Include "VerbLib";
0031   
0032  ! ----------------------------------------------------------------------------
0033  !   The white featureless cubes from "Spellbreaker", which can be identified
0034  !   by being written on with the magic burin, so that their names are given
0035  !   by the player in the course of play
0036  !
0037  !   A particularly witty thing to do is to give several of them the same name,
0038  !   or to frotz some of them to distinguish them from the others...
0039  !   And the game will have no problem with this.
0040  ! ----------------------------------------------------------------------------
0041   
0042  Array cube_text_buffer -> 8;
0043  Global the_named_word = 0;
0044  Global from_char; Global to_char;
0045   
0046  Class  FeaturelessCube
0047    with number 0 0 0 0,   ! There's room for 8 bytes of text in these 4 entries
0048         description "A perfect white cube, four inches on a side.",
0049         parse_name
0050         [ i j flag;
0051              if (parser_action==##TheSame)
0052              {   for (i=0:i<8:i++)
0053                      if ((parser_one.&number)->i
0054                          ~= (parser_two.&number)->i) return -2;
0055                  return -1;
0056              }
0057              for (::i++)
0058              {   j=NextWord(); flag=0;
0059                  if (j=='cube' or 'white' ||
0060                      (j=='featureless' or 'blank' &&
0061                             ((self.&number)->0) == 0)) flag=1;
0062                  if (j=='cubes')
0063                  {   flag=1; parser_action=##PluralFound; }
0064                  if (flag==0 && ((self.&number)->0) ~= 0)
0065                  {   wn--;
0066                      if (TextReader(0)==0) return i;
0067                      for (j=0: j<8: j++)
0068                          if ((self.&number)->j ~= cube_text_buffer->j)
0069                              return i;
0070                      flag=1;
0071                  }
0072                  if (flag==0) return i;
0073              }
0074         ],
0075         article "a",
0076         short_name
0077         [ i; if (((self.&number)->0) == 0) print "featureless white cube";
0078              else
0079              {   print "~";
0080                  while (((self.&number)->i) ~= 0)
0081                      print (char) (self.&number)->i++;
0082                  print "~ cube";
0083              }
0084              rtrue;
0085         ],
0086         plural
0087         [;   self.short_name(); print "s";
0088         ],
0089         baptise
0090         [ i; wn = the_named_word;
0091              if (TextReader(1)==0) return i;
0092              for (i=0: i<8: i++)
0093                  (self.&number)->i = cube_text_buffer->i;
0094              self.article="the";
0095              print_ret "It is now called ", (the) self, ".";
0096         ],
0097    has  scored;
0098   
0099  !  Copies word "wn" from what the player most recently typed, putting it as
0100  !  plain text into cube_text_buffer, returning false if no such word is there
0101   
0102  [ TextReader flag point i j len;
0103   
0104     if (flag==1 && from_char~=to_char)
0105     {   for (i=from_char, j=0:i<=to_char && j<7:i++)
0106         {   cube_text_buffer->j = buffer->i;
0107             if (buffer->i ~= ' ' or ',' or '.') j++;
0108         }
0109         for (:j<8:j++) cube_text_buffer->j = 0;
0110         from_char=0; to_char=0;
0111         rtrue;
0112     }
0113   
0114     for (i=0:i<8:i++) cube_text_buffer->i = 0;
0115     if (wn > parse->1) { wn++; rfalse; }
0116     i=wn*4+1; j=parse->i; point=j+buffer; len=parse->(i-1);
0117   
0118     for (i=0:i<len && i<7:i++) cube_text_buffer->i = point->i;
0119   
0120     wn++; rtrue;
0121  ];
0122   
0123  Object burin "magic burin"
0124    with name "magic" "magical" "burin" "pen",
0125         description
0126            "This is a magical burin, used for inscribing objects with words
0127             or runes of magical import. Such a burin also gives you the
0128             ability to write spell scrolls.",
0129         before
0130         [; WriteOn:
0131               if (second ofclass FeaturelessCube)
0132               {   if (second notin player)
0133                       "Writing on a cube is such a fiddly process that you
0134                        need to be holding it in your hand first.";
0135                   if (burin notin player)
0136                       "You would need some powerful implement for that.";
0137                   second.baptise();
0138                   rtrue;
0139               }
0140               if (second ofclass SpellBook)
0141                   "If a burin could write in a spell book, you wouldn't need
0142                    the gnusto spell!";
0143               if (second ofclass Scroll)
0144                   "You cannot write just anything on the magic parchment of
0145                    a scroll: you can only ~copy~ a spell to it.";
0146         ];
0147   
0148  [ WriteOnSub; "Graffiti is banned."; ];
0149   
0150  [ CopyToSub;
0151    if (burin notin player) "You need to be holding the burin to copy a spell.";
0152    if (second ofclass SpellBook)
0153        "If a burin could write in a spell book, you wouldn't need
0154         the gnusto spell!";
0155    if (~~(second ofclass Scroll)) "You can only copy spells to scrolls.";
0156    if (child(second)~=0)
0157        "The scroll is already full of incantation.";
0158    "The scroll is not blank, only illegible.";
0159  ];
0160   
0161  ! ----------------------------------------------------------------------------
0162  !   Now the whole spell-casting system
0163  ! ----------------------------------------------------------------------------
0164   
0165  Attribute known_about;                 ! Player has seen this spell somewhere
0166  Attribute reversed;                    ! Effect of this known spell reversed
0167   
0168  Attribute is_spell;
0169  Class  Spell
0170    with name "spell" "spells", article "the",
0171         number 0,
0172         word_name
0173         [;  print (address) (self.&name)-->0;
0174         ],
0175         short_name
0176         [;  self.word_name(); print " spell"; give self known_about; rtrue;
0177         ],
0178         specification
0179         [;  self.short_name();
0180             print ": ", (string) self.purpose;
0181         ],
0182         before
0183         [;  Examine: self.specification(); ".";
0184         ],
0185    has  is_spell;
0186   
0187  Object memory
0188    with capacity 5,
0189         number_known 1,
0190         describe_contents
0191         [ i j k;
0192             objectloop (i in self) if (i.number==100) j++;
0193             if (j>0)
0194             {   print "The ";
0195                 objectloop (i in self)
0196                     if (i.number==100)
0197                     {   k++; i.word_name();
0198                         if (k==j-1) print " and ";
0199                         if (k<j-1) print ", ";
0200                     }
0201                 if (j==1) print " spell is"; else print " spells are";
0202                 print " yours forever.  Other than that, y";
0203             }
0204             else print "Y";
0205             print "ou have ";
0206             j=0; k=0;
0207             objectloop (i in self) if (i.number<100) j++;
0208             if (j>0)
0209             {   print "the ";
0210                 objectloop (i in self)
0211                     if (i.number<100)
0212                     {   k++;
0213                         print (name) i;
0214                         if (i.number==2) print " (twice)";
0215                         if (i.number==3) print " (thrice)";
0216                         if (i.number==4) print " (four times)";
0217                         if (i.number>=5) print " (many times)";
0218                         if (k==j-1) print " and ";
0219                         if (k<j-1) print ", ";
0220                     }
0221             }
0222             else print "no spells";
0223             " memorised.";
0224         ],
0225         learn_spell
0226         [ sp;
0227             if (sp.number==100) "You always know that spell.";
0228             print "Using your best study habits, you commit the ";
0229             sp.word_name();
0230             print " spell to memory";
0231             if (sp notin self) sp.number=0;
0232             move sp to self;
0233             self.number_known++;
0234             sp.number++;
0235             if (sp.number==1) print ".";
0236             if (sp.number==2) print " once again.";
0237             if (sp.number==3) print " a third time.";
0238             if (sp.number>3) print " yet another time.";
0239             if (self.number_known <= self.capacity) { new_line; rtrue; }
0240             self.forget_spell(sibling(child(self)));
0241             "  You have so much buzzing around in your head, though,
0242                that it's likely something may have been forgotten
0243                in the shuffle.";
0244         ],
0245         forget_spell
0246         [ sp;
0247             if (sp notin self || sp.number==100) rtrue;
0248             self.number_known--;
0249             sp.number--;
0250             if (sp.number==0) remove sp;
0251             rtrue;
0252         ];
0253   
0254  Spell -> gnusto_spell
0255    with name "gnusto",
0256         purpose "copy a scroll into your spell book",
0257         number 100,
0258         magic
0259         [ i a_book;
0260              if (second ofclass SpellBook)
0261                 "Unlike scrolls, spell books are magically guarded against
0262                  the 'theft' of their lore.";
0263              if (second==0 || ~~(second ofclass Scroll))
0264                 "Your spell fizzles vaguely out.";
0265              if (second notin player)
0266                  "A gnusto spell would require close scrutiny of the scroll
0267                   it is to copy: which you do not seem to be holding.";
0268              objectloop (i in player)
0269                  if (i ofclass SpellBook) a_book=i;
0270              if (a_book==0)
0271                  "Your spell fails, as you have no spell book.";
0272              i=child(second);
0273              if (i==0 || ~~(i ofclass Spell))
0274              {   print_ret "Your spell fails, as ", (the) second,
0275                     " is illegible.";
0276              }
0277              a_book.learn_spell(i); remove second;
0278              print_ret
0279                 "Your spell book begins to glow softly.  Slowly, ornately,
0280                  the words of ", (the) i, " are inscribed,
0281                  glowing even more brightly then the book itself. 
0282                  The book's brightness fades, but the spell remains! 
0283                  However, the scroll on which it was written vanishes as
0284                  the last word is copied.";
0285         ];
0286   
0287  Class SpellBook
0288    with array_of_spells 0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 0,
0289         capacity 16,
0290         learn_spell
0291         [ sp p i;
0292                p = self.&array_of_spells;
0293                for (i=0:i<self.capacity && (p-->i)~=0:i++) ;
0294                if (i==self.capacity) rtrue;
0295                p-->i = sp;
0296         ],
0297         before
0298         [; Open, Close:
0299                print_ret
0300                (The) self, " is always open to the right place, but it
0301                is also always closed. This eliminates tedious leafing and
0302                hunting for spells.  Many lives have been saved by this
0303                magical innovation.";
0304            Attack:
0305                print_ret "When you are done, ", (the) self, " remains unmarred.";
0306         ],
0307         after
0308         [ p i j; Examine:
0309                p = self.&array_of_spells;
0310                for (i=0:i<self.capacity && (p-->i)~=0:i++)
0311                {   j=p-->i; <Examine j>;
0312                }
0313                rtrue;
0314         ];
0315   
0316  Class Scroll
0317    with parse_name
0318         [ i j k; j=-1;
0319                if (self has general)
0320                {   if (child(self)~=0 && child(self) ofclass Spell)
0321                        j=(child(self).&name)-->0; else j='illegible';
0322                }
0323                for (::)
0324                {   k=NextWord();
0325                    if (k=='scrolls') parser_action=##PluralFound;
0326                    if ((k=='scrolls' or 'scroll' or j) || k==(self.&name)-->0)
0327                        i++;
0328                    else return i;
0329                }
0330         ],
0331         before
0332         [ i; Examine:
0333              i=child(self);
0334              give self general;
0335              if (i==0 || ~~(i ofclass Spell))
0336                  "The scroll has faded, and you cannot read it.";
0337              print "The scroll reads ~"; i.specification(); "~.";
0338         ],
0339         invent
0340         [;   if (inventory_stage==2 && self has general)
0341              {   if (child(self)==0 || ~~(child(self) ofclass Spell))
0342                      print " (which is illegible)";
0343                  else
0344                  {   print " (of ", (the) child(self), ")"; }
0345              }
0346         ];
0347   
0348  [ ReadableSpell i j k;
0349    if (scope_stage==1)
0350    {   if (action_to_be==##Examine) rfalse;
0351        rtrue;
0352    }
0353    if (scope_stage==2)
0354    {   objectloop (i in player)
0355            if (i ofclass SpellBook)
0356            {   for (k=0:k<i.capacity && (i.&array_of_spells)-->k~=0:k++)
0357                {   j=(i.&array_of_spells)-->k; PlaceInScope(j);
0358                }
0359            }
0360        rtrue;
0361    }
0362    ! No need for scope_stage 3 (the error stage), because our
0363    ! ParserError routine handles that case instead
0364  ];
0365   
0366  [ CopyableSpell i j k;
0367    if (scope_stage==1) return 1;
0368    if (scope_stage==2)
0369    {   objectloop (i in player)
0370            if (i ofclass SpellBook)
0371            {   for (k=0:k<i.capacity && (i.&array_of_spells)-->k~=0:k++)
0372                {   j=(i.&array_of_spells)-->k; PlaceInScope(j);
0373                }
0374            }
0375        rfalse;
0376    }
0377    ! No need for scope_stage 3 (the error stage), because our
0378    ! ParserError routine handles that case instead
0379  ];
0380   
0381  [ SpellsSub; memory.describe_contents(); ];
0382   
0383  [ LearnSub; if (location==thedark)
0384                  print "(The magic writing of the spells casts enough light
0385                          that you can read them.)^";
0386              memory.learn_spell(noun);
0387  ];
0388   
0389  Global the_spell_was = gnusto_spell;
0390   
0391  [ CastOneSub; <Cast the_spell_was noun>; ];
0392   
0393  [ CastSub;
0394    the_spell_was = noun; memory.forget_spell(noun);
0395   
0396    if (noun has reversed)
0397    {   give noun ~reversed;
0398        if (noun.unmagic() ~= 0) return;
0399        "Nothing happens.";
0400    }
0401   
0402    if (second ~= 0)
0403    {   ResetVagueWords(second);                     ! Set "it", "him", "her"
0404        if (second provides before
0405            && second.before() ~= 0) return;         ! Run before routine(s)
0406    }
0407    if (noun.magic() ~= 0) return;
0408    "Nothing happens.";
0409  ];
0410   
0411  [ InScope i;
0412    if (verb_word=='c,cast' or 'cast')
0413        objectloop (i in memory) PlaceInScope(i);
0414    rfalse;
0415  ];
0416   
0417  [ ParserError x i flag vb;
0418    if (etype==VERB_PE or ASKSCOPE_PE)
0419    {   if (etype==ASKSCOPE_PE)
0420        {   if (verb_word=='cast') vb=1;
0421            if (verb_word=='learn' or 'memorise' or 'memorize') vb=2;
0422            if (verb_word=='copy') vb=3;
0423            if (vb==0) { etype=CANTSEE_PE; rfalse; }
0424        }
0425        wn=verb_wordnum; if (vb~=0) wn++;
0426        x=NextWordStopped();
0427        for (i=player+1:i<=top_object:i++)
0428            if (i ofclass Spell && Refers(i,x)==1
0429                && i has known_about) flag=1;
0430        if (flag==1)
0431        {   if (vb==0 or 1)
0432               "You haven't got that spell committed to memory.  [Type ~spells~
0433                to see what you do remember.]";
0434            if (vb==2)
0435               "Your training is such that you can only memorise such a spell
0436                with the aid of a spell book containing it.";
0437            if (vb==3)
0438               "You have no text of that spell to copy.";
0439        }
0440        if (vb==1)
0441           "You haven't learned that spell, if indeed it is a spell.";
0442        if (vb==2 or 3)
0443           "You haven't access to that spell, if indeed it is a spell.";
0444    }
0445    rfalse;
0446  ];
0447   
0448  [ ChooseObjects obj code;
0449    if (code<2) rfalse;
0450    if (action_to_be==##WriteOn && obj in player) return 9;
0451    return 0;
0452  ];
0453   
0454  [ UnknownVerb word i;
0455    objectloop (i in memory)
0456        if (word==(i.&name)-->0) { the_spell_was = i; return 'c,cast'; }
0457    rfalse;
0458  ];
0459   
0460  [ PrintVerb v;
0461    if (v=='c,cast') { print "cast a spell at"; rtrue; }
0462    rfalse;
0463  ];
0464   
0465  ! ----------------------------------------------------------------------------
0466  !   And now, on with the story.  First, some global variables:
0467  ! ----------------------------------------------------------------------------
0468   
0469  Global prepared_flag = false;      !  Prepared for resurrection?
0470  Global hearing_good  = false;      !  Sharp hearing?
0471  Global number_filled = 0;          !  Sockets in the temple filled
0472   
0473  ! ----------------------------------------------------------------------------
0474  !   A "questions" verb.  Thus,
0475  !      "who is my friend helistar"
0476  !      "what was the great change"
0477  !   and so on are recognised.
0478  ! ----------------------------------------------------------------------------
0479   
0480  [ QuerySub;
0481    noun.description();
0482  ];
0483  [ Topic i;
0484    if (scope_stage==1) return 0;
0485    if (scope_stage==2)
0486    {   objectloop (i ofclass Question) PlaceInScope(i);
0487        rtrue;
0488    }
0489    "At the moment, even the simplest questions confuse you.";
0490  ];
0491   
0492  Class Question;
0493  Question
0494    with name "helistar" "my" "friend" "colleague",
0495         description
0496        "Helistar is your colleague, an Enchanter like you who has been much
0497         on your mind lately.  She has been investigating some very dark
0498         magic indeed, and seems not to be around any more.  You feel rather
0499         vague about the details.";
0500  Question
0501    with name "magical" "magic" "burin",
0502         description "A burin is an engraving and writing tool.";
0503  Question
0504    with name "change" "great",
0505         description
0506        "Something you had a lot to do with, but what exactly?  No, it's gone.";
0507  Question
0508    with name "cyclops",
0509         description
0510        "A one-eyed giant, usually hostile.  (Don't they teach anything at
0511         adventurer school these days?)";
0512  Question
0513    with name "grue",
0514         description
0515        "The grue is a sinister, lurking presence in the dark places of the
0516         earth. Its favorite diet is adventurers, but its insatiable appetite
0517         is tempered by its fear of light. No grue has ever been seen by the
0518         light of day, and few have survived its fearsome jaws to
0519         tell the tale.";
0520  Question
0521    with name "grimoire",
0522         description
0523        "According to Chambers English Dictionary, a grimoire is ~a magician's
0524         book for calling up spirits~.";
0525  Question
0526    with name "spells" "work",
0527         description
0528        "Your memory is still dream-hazed, but it's coming back. As a trained
0529         Enchanter, you have the ability to cast spells like ~frotz~, provided
0530         you have learned them in advance.  Some of these spells can be cast
0531         at something; others can be cast alone.
0532       ^^Spells are very complicated.  Each time you cast one, you forget it
0533         (though you can get around this by learning it several times, so as
0534         to have several uses up your sleeve).  The only way to hang on to
0535         a spell is to have it written down, on a scroll or in a spell book.
0536         Scrolls are not ideal for this, and you can only learn a spell from
0537         your spell book.  (But you can add spells to your book.)
0538       ^^[Type ~learn frotz~ and then ~frotz coin~ for an example.  You can
0539         also type ~spells~ to see what you currently have memorised.]";
0540   
0541  ! ----------------------------------------------------------------------------
0542  !   Some multiple objects, coins in fact, coded in deluxe fashion:
0543  ! ----------------------------------------------------------------------------
0544   
0545  Attribute is_coin;
0546  Class Coin
0547    with name "coin",
0548         description "A round unstamped disc, presumably part of the local
0549             currency.",
0550         parse_name
0551         [ i j w;
0552           if (parser_action==##TheSame)
0553           {   if ((parser_one.&name)-->0 == (parser_two.&name)-->0) return -1;
0554               return -2;
0555           }
0556           w=(self.&name)-->0;
0557           for (::i++)
0558           {   j=NextWord();
0559               if (j=='coins') parser_action=##PluralFound;
0560               else if (j~='coin' or w) return i;
0561           }
0562         ],
0563    has  is_coin;
0564   
0565  Class GoldCoin
0566   class Coin,
0567    with name "gold",
0568         short_name "gold coin",
0569         plural "gold coins";
0570  Class SilverCoin
0571   class Coin,
0572    with name "silver",
0573         short_name "silver coin",
0574         plural "silver coins";
0575  Class BronzeCoin
0576   class Coin,
0577    with name "bronze",
0578         short_name "bronze coin",
0579         plural "bronze coins";
0580   
0581  SilverCoin players_coin;
0582   
0583  [ TossCoinSub; if (noun notin player) "You need to be holding the coin first.";
0584    move noun to parent(player);
0585    if (location==thedark) "You throw it away into the darkness.";
0586    if (random(20)==1)
0587       "You toss the coin, and it lands... on its edge, amazingly.";
0588    "You toss the coin, and it comes up... blank, since neither side is
0589     marked.";
0590  ];
0591   
0592  ! ----------------------------------------------------------------------------
0593  !   The player's spell book, and three initial spells (to go with gnusto):
0594  ! ----------------------------------------------------------------------------
0595   
0596  SpellBook players_book "spell book"
0597    with name "spell" "book" "my" "spellbook",
0598         description "My Spell Book^";
0599   
0600  Spell frotz_spell
0601    with name "frotz",
0602         purpose "cause an object to give off light",
0603         magic
0604         [;  if (second==0) "There is a brief, blinding flash of light.";
0605             if (second has animate)
0606                 "The spell, not designed for living creatures, goes sour.";
0607             if (second in compass)
0608                 "The spell dissipates vaguely.";
0609             give second light;
0610             print_ret
0611              "There is an almost blinding flash of light as ", (the) second,
0612              " begins to glow!  It slowly fades to a less painful level, but ",
0613              (the) second, " is now quite usable as a light source.";
0614         ],
0615         unmagic
0616         [;  if (second==0) "There is a brief moment of deep darkness.";
0617             if (second has animate)
0618                 "The spell, not designed for living creatures, goes sour.";
0619             if (second in compass)
0620                 "The spell dissipates vaguely.";
0621             if (second hasnt light)
0622                 print_ret (The) second, " isn't producing light as it is.";
0623             give second ~light;
0624             print_ret "A pool of darkness coagulates around ", (the) second,
0625                   " but slowly fades back to normality.  Still, ",
0626                   (the) second, " is no longer any kind of light source.";
0627         ];
0628   
0629  Spell rezrov_spell
0630    with name "rezrov",
0631         purpose "open even locked or enchanted objects",
0632         magic
0633         [;  if (second==0) "The world is open already.";
0634             if (second has animate)
0635                 "It might be a boon to surgeons if it worked, but it doesn't.";
0636             if (second has open || second hasnt openable)
0637                 "It doesn't need opening.";
0638             if (second hasnt locked)
0639             {   give second open;
0640                 print_ret (The) second, " opens obediently. 
0641                 Like swatting a fly with a sledge hammer, if you ask me.";
0642             }
0643             give second open ~locked;
0644             print "Silently, ", (the) second, " swings open. ";
0645             if (second has container) <<Search second>>; new_line; rtrue;
0646         ],
0647         unmagic
0648         [;  if (second==0) "The world is closed already.";
0649             if (second has animate)
0650                 "Happily, that is unnecessary.";
0651             if (second has locked || second hasnt lockable)
0652                 "It doesn't need locking.";
0653             give second ~open locked;
0654             "Silently, ", (the) second, " swings shut and locks.";
0655         ];
0656   
0657  Spell yomin_spell
0658    with name "yomin",
0659         purpose "mind probe",
0660         magic
0661         [;  if (second==0 || second hasnt animate)
0662                 "That must be either vegetable or mineral.";
0663             if (second==player) "You give yourself a mild headache.";
0664             print_ret "You look into the rather ordinary thoughts of ",
0665                       (the) second, ".";
0666         ],
0667         unmagic
0668         [;  if (second==0 || second hasnt animate)
0669                 "That must be either vegetable or mineral.";
0670             if (second==player) "You give yourself a mild headache.";
0671             print_ret (The) second, " is rather shocked, for some reason.";
0672         ];
0673   
0674  ! ----------------------------------------------------------------------------
0675  !   The first scene: the Hut and its (rather easy) secret 
0676  ! ----------------------------------------------------------------------------
0677   
0678  Class Place
0679    has  light;
0680   
0681  Place Hut "Ramshackle Hut"
0682    with description
0683            "Until quite recently, someone lived here, you feel sure. 
0684             Now the furniture is matchwood and
0685             the windows are glassless.  Outside, it is a warm, sunny day,
0686             and grasslands extend to the low hills on the horizon.",
0687         out_to Grasslands, w_to Grasslands,
0688         cant_go "There's only the one room: better go ~out~.",
0689         name "windows" "grasslands" "grass" "hills";
0690   
0691  Object -> furniture "wooden furniture"
0692    with name "furniture" "broken" "wood" "wooden",
0693         before
0694         [;  Examine, Search, LookUnder:
0695                 self.before=0; score=score+5;
0696                 move h_box to player;
0697                 "Searching through the furniture, which is good for nothing
0698                  but firewood now, you come across an old cedarwood box,
0699                  which you pick up for a closer look.";
0700         ],
0701    has  scenery;
0702   
0703  Object h_box "cedarwood box"
0704    with name "cedar" "cedarwood" "wooden" "box",
0705         description "The box bears the calligraphed initial H."
0706    has  container openable lockable locked;
0707   
0708  SpellBook -> helistars_book "Helistar's grimoire"
0709    with name "grimoire" "helistar" "helistars",
0710         description "This must be the grimoire of dangerous spells kept by
0711                      your irresponsible friend Helistar.  Many pages are
0712                      missing, but a few spells remain:^",
0713    has  proper;
0714   
0715  ! ----------------------------------------------------------------------------
0716  !   Grasslands and the valley
0717  ! ----------------------------------------------------------------------------
0718   
0719  Place Grasslands "Grasslands, near Hut"
0720    with name "grasslands" "grass" "hut" "path",
0721         description
0722            "The grasslands sway over low hills in all directions: it is a
0723             peaceful wilderness, broken only by this hut and a faint path
0724             to the north.",
0725         in_to Hut, e_to Hut,
0726         n_to Valley,
0727         cant_go "You wander around for a while but end up back at the hut.";
0728   
0729  Place Valley "Pocket Valley"
0730    with name "valley" "trail",
0731         description
0732            "A pleasant pocket valley in the grassy hills, through which a
0733             trail runs north-to-south.",
0734         n_to "The trail runs out to nothing, and you retreat for fear of
0735               getting so lost you couldn't find the hut again by nightfall.",
0736         cant_go "You wander around the pleasant valley, but are afraid to
0737                  lose sight of the trail.",
0738         s_to Grasslands;
0739   
0740  [ RideSub; print_ret "You can hardly ride ", (a) noun, "."; ];
0741   
0742  Object -> horse "horse"
0743    with short_name
0744         [; if (self has general) print "winged horse";
0745            else print "chestnut horse";
0746            rtrue;
0747         ],
0748         parse_name
0749         [ i j; if (self has general) j='winged'; else j=-1;
0750            while (NextWord()==j or 'horse' or 'chestnut') i++;
0751            return i;
0752         ],
0753         describe
0754         [;  print_ret
0755                "There is ", (a) self, " here, munching on a pile of oats.";
0756         ],
0757         before
0758         [;  Cast: if (the_spell_was == bozbar_spell)
0759                   {   give self general;
0760                      "A pair of handsome brown wings suddenly appears on
0761                       the horse's powerful shoulders.  The horse turns in a
0762                       complete circle, a look of puzzlement on his face.";
0763                   }
0764                   if (the_spell_was == yomin_spell)
0765                      "He is mainly thinking about oats.  Partly who you are
0766                       and what you're up to, but mainly oats.";
0767             Enter: <<Ride self>>;
0768             Ride: if (horse hasnt general)
0769                      "You ride around for a while, exercising the horse, but
0770                       soon enough he tires of this and pointedly brings you
0771                       back to the oats.  Obligingly you dismount and he
0772                       begins grazing again.";
0773   
0774             print "You begin to ride north.  Then, slowly at first but with
0775                    increasing sureness, the horse begins beating its powerful
0776                    wings.  You rise majestically through the air, sailing
0777                    gracefully across a chasm where the hills fall away. 
0778                    The horse lands gently on the far side and deposits you,
0779                    taking to the skies again.^";
0780             PlayerTo(Edge); rtrue;
0781         ],
0782    has  animate;
0783   
0784  Object -> oats "pile of oats"
0785    with name "oats" "pile" "of",
0786         before
0787         [;  Examine, Search, LookUnder:
0788                 self.before=NULL;
0789                 move shiny_scroll to player; score=score+5;
0790                 itobj=shiny_scroll;
0791                 "Sifting through the oats, you find a shiny scroll!  Lucky
0792                  you got to it before the horse did.  As you turn it over
0793                  in your hands, it seems undamaged.";
0794             Take:  "What would you want with all those oats?";
0795         ],
0796    has  scenery;
0797   
0798  Scroll shiny_scroll "shiny scroll"
0799    with name "shiny";
0800   
0801  Spell -> bozbar_spell
0802    with name "bozbar",
0803         purpose "cause an animal to sprout wings",
0804         magic
0805         [;  if (second==0 || second hasnt animate)
0806                 "The spell dies away in vain.";
0807             if (second==player)
0808                 "Your elbows twitch, but there is no other effect.";
0809             print_ret "For a moment, ", (the) second,
0810                 " looks highly discomforted, but the moment passes.";
0811         ],
0812         unmagic
0813         [;  if (second==0 || second hasnt animate)
0814                 "The spell dies away in vain.";
0815             if (second==player) "What wings?";
0816             if (second==horse && horse has general)
0817             {   give horse ~general;
0818                 "The Enchanter giveth, and the Enchanter taketh away. 
0819                  The horse looks disconsolate but returns to the oats.";
0820             }
0821             print_ret (The) second, " has no wings to lose.";
0822         ];
0823   
0824  ! ----------------------------------------------------------------------------
0825  !   The Chasm and the snake
0826  ! ----------------------------------------------------------------------------
0827   
0828  Place Edge "Edge of Chasm"
0829    with name "wide" "chasm" "road" "daffodils" "clump",
0830         description
0831            "The road ends suddenly at a wide chasm.  The road leads upward
0832             to the north, and you can see it continuing on the southern side
0833             of the chasm.",
0834         u_to Up_Road, n_to Up_Road,
0835         cant_go "The chasm is too perilous to approach.  The only safe way is
0836                 up and to the north.",
0837         before
0838         [;  Jump: deadflag = true;
0839                "You jump bravely into the chasm, and plunge...
0840                 gracefully through the air.  (It gets a bit less noble and
0841                 airy after that.)";
0842         ];
0843   
0844  Object -> snake "hissing snake"
0845    with name "hissing" "snake",
0846         initial
0847           "Lying in a tight coil at the edge of the chasm is a hissing snake.",
0848         description
0849           "It has some V-markings, some scaly parts, colours from grey to
0850            reddish-brown. Is that any help?",
0851         life
0852         [; "The snake hisses angrily!"; ],
0853         before
0854         [;  Cast:
0855                 switch(the_spell_was)
0856                 {   urbzig_spell:
0857                         remove self;
0858                         snakes_cube.initial =
0859                      "Beside a clump of daffodils is a featureless white cube.";
0860                         "The snake is replaced by a clump of daffodils.";
0861                     bozbar_spell:
0862                         deadflag = true; remove self;
0863                         snakes_cube.initial =
0864                        "A featureless cube rests where the snake took off from.";
0865                        "The snake is transformed into a huge, winged serpent,
0866                         a dragon which bellows and leaps out into the chasm,
0867                         backwinging furiously... and knocking you over the
0868                         edge quite by accident.";
0869                     yomin_spell:
0870                        "Horrid reptilian thoughts insinuate their way into you.";
0871                 }
0872             Take, Remove:
0873                "The slipperiness of its skin is only one of many reasons
0874                 why this is ill-advised.";
0875         ],
0876    has  animate;
0877   
0878  FeaturelessCube -> snakes_cube "cube"
0879    with initial
0880           "The snake appears to be curled around a featureless white cube.",
0881         before
0882         [; if (snake notin nothing) "The snake won't let you near that cube!";
0883         ];
0884   
0885  ! ----------------------------------------------------------------------------
0886  !   The crest of the hill; Icarus the tortoise; the chewed scroll
0887  ! ----------------------------------------------------------------------------
0888   
0889  Place Up_Road "Crest of Hill"
0890    with description
0891            "The road crosses the top of a ridge here, sloping downwards to
0892             the south and the northwest.  A track diverges to east.",
0893         nw_to Cave_Mouth, s_to Edge, d_to Edge, e_to Track;
0894   
0895  Object -> tortoise "tortoise"
0896    with name "tortoise" "turtle",
0897         initial "A tortoise ambles along the road, extremely slowly.",
0898         life
0899         [; "The tortoise (slowly) turns its neck to look at you (stupidly).";
0900         ],
0901         before
0902         [; Cast: switch(the_spell_was)
0903                  {   urbzig_spell:
0904                         "Just how safe do you want your surroundings to be?";
0905                      bozbar_spell:
0906                          move chewed_scroll to parent(self); remove self;
0907                          StartDaemon(self); score=score+5;
0908                         "The tortoise seems to be incapable of expressing
0909                          surprise, but is now soaring away high in the sky. 
0910                          Something rather grubby is left behind.";
0911                      yomin_spell:
0912                         "For a moment you think there is nothing there, as you
0913                          chew absentmindedly on a leaf.  But somewhere inside
0914                          the tortoise is a sense of wonder at the amazing blue
0915                          canopy of the sky.";
0916                  }
0917            Take, Remove:
0918                 "Your parents always warned you not to pick up casual
0919                  acquaintances met on the road.";
0920         ],
0921         daemon
0922         [ i; if (location ~= Up_Road or Track || random(6)~=1) rfalse;
0923            if (random(4)==1 && self hasnt general)
0924            {   move feather to location; give self general;
0925                "^A tortoise-feather flutters to the ground before you!";
0926            }
0927            i=random(3);
0928            switch(i)
0929            {   1: print "^High in the sky,";
0930                2: print "^Far above you,";
0931                3: print "^Tiny in the blue sky,";
0932            }
0933            " a tortoise flaps across the sun.";
0934         ],
0935    has  animate;
0936   
0937  Scroll torn_scroll "torn scroll"
0938    with name "torn";
0939   
0940  Spell -> lobal_spell
0941    with name "lobal",
0942         purpose "sharpen hearing",
0943         magic
0944         [;  if (second==0 || second hasnt animate)
0945                 "There is a loud bang in your ear, but no other effect.";
0946             if (second==player)
0947             {   if (hearing_good) "There is no further effect.";
0948                 hearing_good=1; StartTimer(self, 5);
0949                 "Nothing happens, possibly because those butterflies on the
0950                  other side of the hill keep distracting you.";
0951             }
0952             print_ret (The) second,
0953                " is no doubt grateful for the gift of sharper hearing.";
0954         ],
0955         unmagic
0956         [;  if (second==0 || second hasnt animate)
0957                 "There is a brief silence, but no other effect.";
0958             if (second==player) { StopTimer(self); hearing_good=0; "Pardon?"; }
0959             print_ret (The) second,
0960                " is no doubt grateful not to have to listen to you.";
0961         ],
0962         time_left 0,
0963         time_out
0964         [;  if (hearing_good)
0965             {   hearing_good = false;
0966                 "^Those wretched butterflies finally shut up.";
0967             }
0968         ];
0969   
0970  Scroll chewed_scroll "chewed scroll"
0971    with initial "It looks as if the tortoise was eating something - once
0972                  it might have been a scroll, but now it lies there,
0973                  chewed up like a lettuce leaf.",
0974         before
0975         [;  Cast: if (the_spell_was == caskly_spell)
0976                   {   move torn_scroll to parent(self);
0977                       remove self; score=score+5;
0978                       "Before your eyes, the scroll begins to repair itself,
0979                        failing only at the very last tear.  Not quite perfect
0980                        perhaps, but certainly a readable, if torn scroll.";
0981                   }
0982             Eat:  "~Eating your words~ is notoriously dangerous for a wizard.
0983                    Rearranged in the stomach, a spell might do anything!";
0984         ],
0985    with name "chewed";
0986   
0987  Object feather "tortoise feather"
0988    with name "tortoise" "feather",
0989         description
0990            "Possibly your rarest, and also least valuable, possession.";
0991          
0992  ! ----------------------------------------------------------------------------
0993  !   The cave mouth and the perfect sapphire
0994  ! ----------------------------------------------------------------------------
0995   
0996  Place Cave_Mouth "Cave Mouth"
0997    with name "gorse" "footpath" "cave" "mouth",
0998         description
0999            "This is a cave mouth, at one end of a road which winds southeast
1000             over rising ground.  The entrance west to the caves is a dark
1001             tunnel, and only a footpath runs further north, into gorse.",
1002         u_to Up_Road, se_to Up_Road, in_to Iron_Door, w_to Iron_Door,
1003         n_to Footpath;
1004   
1005  Object -> Iron_Door "iron door"
1006    with name "iron" "door" "heavy",
1007         description "It just looks like an ordinary heavy iron door.",
1008         door_dir
1009         [; if (location==Cave_Mouth) return w_to; return e_to;
1010         ],
1011         door_to
1012         [; if (location==Cave_Mouth) return In_Cave;
1013            return Cave_Mouth;
1014         ],
1015         describe
1016         [; if (self has open) "^The iron door stands open.";
1017            if (self hasnt locked) "^The iron door is unlocked but shut.";
1018            "A heavy iron door bars the cave mouth.";
1019         ],
1020         found_in  In_Cave  Cave_Mouth
1021    has  static door openable locked lockable;
1022   
1023  !  Cf. T. S. Eliot, "Burnt Norton" II:
1024  !  (but see also Mallarme's sonnet from which Eliot borrowed the image)
1025   
1026  Object -> sapphire "perfect sapphire"
1027    with name "perfect" "sapphire" "gemstone" "gem",
1028         initial "Clotted in the mud beside the door is a perfect sapphire.",
1029         before
1030         [;  Examine: remove self; move caskly_spell to memory;
1031                      players_book.learn_spell(caskly_spell);
1032                      caskly_spell.number=100;
1033                      "As you gaze into the perfect blue of the sapphire,
1034                       you feel your mind begin to reel.  Unable to bear
1035                       the naked sight of perfection, you look away, ashamed. 
1036                       As you do so, the sapphire cracks and wastes away to
1037                       thin hot dust.  But something remains, something in your
1038                       mind...";
1039         ];
1040   
1041  Spell caskly_spell
1042    with name "caskly",
1043         purpose "cause perfection",
1044         magic
1045         [;  if (second==0) "Trying to make everything perfect was a little
1046                             too ambitious.";
1047             if (second==player) "Oh, don't be too hard on yourself.";
1048             if (second==helistars_book)
1049                 "Your spell is not powerful enough to restore the lost pages.";
1050             print_ret (The) second, " looks pretty perfect as is.";
1051         ];
1052   
1053  ! ----------------------------------------------------------------------------
1054  !   Inside the Cave, the powerful urbzig spell and its consequences
1055  ! ----------------------------------------------------------------------------
1056   
1057  Place In_Cave "Inside Cave"
1058    with description
1059            "A wide but shallow cave not far inside the hill.  There is no
1060             obvious exit, except for the way you came in.",
1061         out_to
1062         [;  if (CoinsIn(left_pan)+CoinsIn(right_pan) < 6)
1063                 "Something bars your way, and you hear
1064                  the scales jangling militantly.  You were trying to
1065                  steal its coins!";
1066             if (scales.number~=0) "Something bars your way, and you hear
1067                 the scales jangle slightly with energy.";
1068             return Iron_Door;
1069         ],
1070         e_to
1071         [;  return self.out_to();
1072         ],
1073         cant_go "The only way is back ~out~ through the iron door.",
1074         after
1075         [;  Take: if (parent(noun)==left_pan or right_pan)
1076                       print_ret "Taken from ", (the) parent(noun), ".";
1077         ],
1078    has  ~light;
1079   
1080  FeaturelessCube -> cave_cube "cube"
1081    with initial "Balanced on a rock formation is a featureless white cube.";
1082   
1083  Object -> scales "pair of scales"
1084    with name "pair" "of" "scales" "pans", number 0,
1085         describe
1086         [;  print "^A fair-sized pair of scales hangs from a bracket in the
1087                    cave wall.  ";
1088             if (self.number==0)  "The scales are balanced.";
1089             if (self.number==1)  "The left-hand side is higher.";
1090             "The right-hand side is higher.";
1091         ],
1092         before
1093         [;  "There are left and right hand pans, which you should refer to
1094              individually.";
1095         ],
1096    has  static supporter;
1097   
1098  Class  ScalePan
1099    with name "pan" "side" "tray",
1100         before
1101         [;  Receive:
1102             if (noun ofclass Scroll or Coin) rfalse;
1103             if (noun==feather) rfalse;
1104             "The pans gleam with what almost seems greed, and somehow they
1105              contrive to nudge your hand past them with your worthless and
1106              boring item.";
1107         ],
1108         after
1109         [ i j d w1 w2;  Receive, LetGo: i=scales.number;
1110             objectloop (j in left_pan) w1=w1 + WeightOf(j);
1111             objectloop (j in right_pan) w2=w2 + WeightOf(j);
1112             if (w1==w2) scales.number=0;
1113             if (w1 > w2) scales.number=-1;
1114             if (w1 < w2) scales.number=1;
1115             j=scales.number; d=(w2-w1)*(scales.number);
1116             if (j==i) rfalse;
1117             if (j==0) "The scales come into balance.";
1118             if (j==1) print "The left pan "; else print "The right pan ";
1119             if (d==1) "very slowly rises up.";
1120             "rises up.";
1121         ],
1122    has  supporter scenery;
1123   
1124  [ WeightOf obj;
1125    if (obj==bronze_coin) return 2;
1126    if (obj ofclass Scroll || obj==feather) return 1;
1127    return 3;
1128  ];
1129   
1130  [ CoinsIn obj i c;
1131    objectloop (i in obj) if (i ofclass Coin) c++;
1132    return c;
1133  ];
1134   
1135  ScalePan -> right_pan "right pan" with name "right";
1136  GoldCoin -> ->;
1137  GoldCoin -> ->;
1138  GoldCoin -> ->;
1139   
1140  ScalePan -> left_pan "left pan" with name "left";
1141  BronzeCoin -> -> bronze_coin;
1142  GoldCoin -> ->;
1143  GoldCoin -> ->;
1144   
1145  Scroll -> -> crumpled_scroll "crumpled scroll"
1146    with name "crumpled";
1147  Spell  -> -> -> urbzig_spell
1148    with name "urbzig",
1149         purpose "turn a dangerous object into a harmless one",
1150         magic
1151         [;  if (second==0) "The spell fizzles away.";
1152             if (second==player) "It's a matter of opinion, isn't it?";
1153             if (second==helistars_book or mace || second ofclass FeaturelessCube)
1154             {   CDefArt(second); remove second;
1155                 if (second==mace && cyclops in location)
1156                 {   remove cyclops; move eye_cube to location;
1157                     " turns into a featureless white cube just as the cyclops
1158                      was about to hit you with it.  Mightily embarrassed
1159                      by this, he drops the cube and runs off!";
1160                 }
1161                 print " turns into a moth and flutters away.^";
1162                 rtrue;
1163             }
1164             print_ret "Nothing obvious happens.  Perhaps ", (the) second,
1165                       " isn't so very dangerous after all.";
1166         ],
1167         unmagic
1168         [;  if (second==0) "The spell fizzles away.";
1169             if (second==player) "It's a matter of opinion, isn't it?";
1170             if (second has static || second has scenery)
1171             {   print_ret "Your spell is too weak for something quite as
1172                            monumentally harmless as ", (the) second, ".";
1173             }
1174             if (second==helistars_book or snake or cyclops or mace
1175                 || second ofclass FeaturelessCube)
1176                 "Nothing obvious happens.";
1177             if (second in player)
1178             {   remove second; deadflag = true;
1179                 "Suddenly, a tarantula races up your arm to your throat! 
1180                  Perhaps it was unwise to gizbru something you were
1181                  actually holding.";
1182             }
1183             if (cyclops has general)
1184                 "Nothing happens.  Perhaps that's just as well,
1185                  after the last time.";
1186             move cyclops to location;
1187             remove second; give cyclops general; StartTimer(cyclops, 5);
1188             print_ret (The) second, " is replaced by a buck-toothed cyclops
1189                  wielding a mace!";
1190         ];
1191   
1192  Object cyclops "buck-toothed cyclops"
1193    with name "buck" "toothed" "buck-toothed" "cyclops",
1194         initial "A huge buck-toothed cyclops menaces you, armed with a
1195                  heavy mace!",
1196         before
1197         [;  Cast: if (the_spell_was == bozbar_spell)
1198                       "Does the term ~death wish~ mean anything to you?";
1199                   if (the_spell_was == urbzig_spell)
1200                       "The cyclops bellows with glee as your spell has
1201                        no effect.  (After all, he wouldn't be ~dangerous~ if
1202                        an urbzig spell worked on him, would he?)";
1203         ],
1204         life [; "He roars incoherently, swinging the mace!"; ],
1205         time_left 0,
1206         time_out
1207         [;  if (self notin location)
1208             {   remove self; rtrue;
1209             }
1210             deadflag = true; remove mace; remove cyclops;
1211             "Feeling that he's given you quite long enough to explain why
1212              you made such a mess of his life, he swings the great mace
1213              maniacally down on you!";
1214         ],
1215         each_turn
1216         [ i; i=random(4); if (i==1) "^The cyclops leaps and bellows!";
1217              if (i==2)
1218                "^Whirling the mace, the cyclops jabbers at you incoherently.";
1219              if (i==3)
1220                "^The cyclops is losing patience (the appropriate cyclops
1221                  word is untranslatable into English, but approximately means
1222                  ~forbearance in not smashing all nearby skulls~).";
1223              "^The cyclops jabs you with the mace, almost breaking your rib.";
1224         ],
1225    has  animate transparent;
1226   
1227  Object -> mace "mace"
1228    with name "heavy" "mace" "axe",
1229         description "It looks much too heavy for you to even lift.";
1230   
1231  FeaturelessCube -> eye_cube "cube"
1232    with initial
1233           "A featureless white cube lies where the cyclops dropped it.";
1234   
1235  ! ----------------------------------------------------------------------------
1236  !   The Footpath and the carpet
1237  ! ----------------------------------------------------------------------------
1238   
1239  Place Footpath "Gorse Bushes"
1240    with description
1241             "The footpath from the cave mouth runs into dense, impenetrable
1242              gorse bushes.  Perhaps it wasn't so much a footpath as a rill
1243              in the earth where roots wouldn't take; anyway, there's no way
1244              but back south.",
1245         s_to Cave_Mouth;
1246   
1247  Object -> carpet "beautiful red carpet"
1248    with name "beautiful" "magic" "red" "carpet",
1249         initial
1250            "Slung over one of the gorse bushes is a beautiful red carpet.",
1251         description
1252            "This is a carpet of unusual design. It is red, beautifully woven
1253             and bears a pattern of cubes.",
1254         before
1255         [ i;  Receive:
1256                   if (self notin location || self hasnt moved)
1257                       "Not until the carpet's on the ground, you can't.";
1258             Ride: <<Enter self>>;
1259             Enter:
1260                   if (self notin location || self hasnt moved)
1261                       "Not until the carpet's on the ground, you can't.";
1262                   if (location==Balance_Room)
1263                       "Mysteriously, the carpet rucks and pulls until you're
1264                        thrown off.  It settles back on the white floor with a
1265                        contented sigh.";
1266                   if (location==In_Cave)
1267                       "The carpet rises suddenly, crashing into the roof of
1268                        the cave and throwing you back off again.  Painfully.";
1269                   if (location==Bazaar) i=Up_Road; else i=Bazaar;
1270                   print "The carpet rises suddenly into the fluffy white
1271                          clouds, and after a headlong journey deposits you...^";
1272                   move self to i;
1273                   PlayerTo(i,1); move player to self; <<Look>>;
1274             Take: if (player in self) "Not while you're on it!";
1275                   for (i=child(self):i~=0:i=child(self))
1276                   {   move i to location;
1277                       print "(Dislodging ", (the) i, ")^";
1278                   }
1279         ],
1280    has  supporter enterable;
1281   
1282  ! ----------------------------------------------------------------------------
1283  !   A Bazaar Lottery
1284  ! ----------------------------------------------------------------------------
1285   
1286  Global last_called = 1;
1287  Global explicit_flag = 0;
1288  Global tickets_taken = 0;
1289   
1290  Class Ticket(6)
1291    with number -1, name "ticket",
1292         description
1293         [;  if (self.number==2306) "It is labelled ~First Prize~!";
1294             if (self.number==5802) "It is labelled ~Nineteenth Prize~.";
1295             "~You lose,~ says the ticket, with a smily face.  ~Try again!~";
1296         ],
1297         short_name
1298         [;  if (self.number==-1) rfalse;
1299             print "lottery ticket ", self.number; rtrue;
1300         ],
1301         parse_name
1302         [ i j w;
1303             i=0;
1304             if (NextWord()=='lottery') i++; else wn--;
1305             if (NextWord()=='tickets')
1306             {   parser_action=##PluralFound; return i+1; } else wn--;
1307             if (NextWord()~='ticket') return 0;
1308             i++;
1309             w=TryNumber(wn);
1310             if (w==-1000) { explicit_flag = false; return i; }
1311             if (w==0) return 0;
1312             if (self.number==-1)
1313             {   objectloop(j ofclass Ticket)
1314                     if (w == j.number) return 0;
1315             }
1316             else
1317             {   if (self.number~=w) return 0;
1318             }
1319             i++; last_called = w; explicit_flag = true; return i;
1320         ],
1321         before
1322         [;  Examine:
1323                 if (self in board)
1324                    "It would be cheating to see what's written on the curled up
1325                     tickets still in the board.";
1326             Cast: "~Get outta here, bub!~, the barker says, disgusted.";
1327         ];
1328   
1329  Place Bazaar "Crowded Bazaar"
1330    with description
1331             "This is a crowded, noisy bazaar.  Directly in front of you is
1332              a lottery!  But the contemptuous-looking barker is doing a
1333              very poor trade: hardly anyone wants his first prize, the
1334              big cuddly toy elephant, or even his nineteenth prize, a
1335              featureless white cube.",
1336         each_turn
1337         [;   switch(random(4))
1338              {   1: "^~Roll up!  Roll up!  One silver piece for three goes!~";
1339                  2: "^~Come on, then!  Just a silver coin gets you three!~";
1340                  3: "^~Think what you could win, all for one silver coin!~";
1341                  4: "^~This could be your lucky day!~";
1342              }
1343         ],
1344         before
1345         [;   Learn:
1346                  "~None of that!~ snaps the barker angrily, putting you off
1347                   your study habits.  He mutters about ~Enchanter cheats~,
1348                   but under the circumstances you decide to let the insult
1349                   pass.";
1350         ],
1351         cant_go "Everywhere, the crowds of jabbering natives block your way
1352             to all the good stalls.  In fact, the only one you can get at is
1353             this dismal lottery.";
1354   
1355  Object -> board "lottery board"
1356    with credit 0,
1357         name "board" "lottery" "holes",
1358         description
1359             "There are a hundred holes each way, making, um, let's see, yes,
1360              ten thousand tickets in all. Still, there are nineteen prizes,
1361              so your odds must be, oh, well, not too awful anyway.",
1362         before
1363         [ i; LetGo:
1364                  if (self.credit == 0)
1365                     "The barker stabs you in the chest with
1366                      his finger.  ~That's a silver coin to you, bub!~";
1367   
1368                  if (explicit_flag)
1369                  {   objectloop (i ofclass Ticket)
1370                          if (last_called == i.number)
1371                              "That ticket's already taken.";
1372                  }
1373                  else
1374                  {   .RandomChoice;
1375                      last_called = random(10000);
1376                      objectloop (i ofclass Ticket)
1377                          if (last_called == i.number)
1378                              jump RandomChoice;
1379                  }
1380   
1381                  tickets_taken++;
1382                  self.credit--;
1383   
1384                  i = Ticket.create();
1385                  if (i == 0)
1386                     "The barker looks metaphysically embarrassed. ~Um,
1387                      Inform's object creation system seems not to have worked.~";
1388   
1389                  i.number = last_called; itobj = i;
1390   
1391                  move i to player; give i moved proper;
1392                  if (explicit_flag==0)
1393                      print "Randomly picking from the ", 10001-tickets_taken,
1394                            " numbered holes with tickets in, you ";
1395                      else print "You ";
1396                  print_ret "take ", (the) i, " out of the board.";
1397   
1398              Examine: ;
1399              Receive:
1400                  if (noun ofclass Ticket)
1401                     "~No changes of mind, that's your ticket now!  Give it to
1402                       me if you want to play it.~";
1403                  <<Push self>>;
1404              default:
1405                  "The barker is burly, and won't let you
1406                   tamper with the board.";
1407         ],
1408         initial
1409             "Behind the barker is a huge drilled board, and inside each little
1410              numbered hole is a rolled-up lottery ticket."
1411    has  static container open;
1412   
1413  Ticket -> -> ticket_in_board "rolled-up ticket from the board"
1414    with article "a";
1415   
1416  Object -> barker "barker"
1417    with name "barker" "burly" "man",
1418         number 0,
1419         description
1420             "A boxer gone to seed who failed as a magician all down the
1421              coast, that'd be your guess.",
1422         life
1423         [;  Attack, Kiss: "No way.  He must weigh twice what you do.";
1424             Ask:  switch(noun)
1425                   {   'prize', 'prizes':
1426                           "~Just one silver coin and a prize could be yours!~";
1427                       'white', 'featureless', 'cube':
1428                           "He blows the dust off it.  ~Genuine antique, that.~";
1429                       'elephant', 'toy', 'cuddly':
1430                           "~Good quality merchandise,~ he says, in a way that
1431                            suggests he can only spell one of those three words.";
1432                       'ticket', 'tickets', 'lottery':
1433                           "~Three tickets for one silver coin!~";
1434                       default: "~Just play the game, bub.~";
1435                   }
1436             Order, Answer: "The barker glowers at you.";
1437             Give: if (noun ofclass Ticket)
1438                   {   remove noun;
1439                       if (noun.number==2306)
1440                       {   move elephant to player; give elephant moved;
1441                           remove pelephant;
1442                           Bazaar.description =
1443             "This is a crowded, noisy bazaar.  Directly in front of you is
1444              the lottery!";
1445                           "With very bad grace, the barker shoves the
1446                            cuddly toy elephant into your arms.";
1447                       }
1448                       if (noun.number==5802)
1449                       {   move barker_cube to player; give barker_cube moved;
1450                           remove pcube;
1451                           Bazaar.description =
1452             "This is a crowded, noisy bazaar.  Directly in front of you is
1453              the lottery!";
1454                           score=score+5;
1455                           "With concealed relief, the barker shoves the
1456                            featureless white cube into your hands.";
1457                       }
1458                       "~Bad luck!  You lose!~";
1459                   }
1460                   if (self.number==2) "~You've had enough goes already!~ he
1461                       growls.  No wonder trade is bad.";
1462                   if (~~(noun ofclass Coin))
1463                       "~What do you call that? One silver coin to play!~";
1464                   if ((noun.&name)-->0 == 'bronze')
1465                       "~Bronze!  Not a chance, sunshine.~";
1466                   remove noun;
1467                   board.credit = board.credit + 3;
1468                   self.number++;
1469                   if ((noun.&name)-->0 == 'gold')
1470                       "Gleefully the barker snatches the gold coin. ~Sorry
1471                        bub, no change. Business is slack today!~";
1472                   "Grudgingly the barker takes the silver coin and stands
1473                    back to let you at the board, arms folded.";
1474         ],
1475         before
1476         [;  Cast: switch(the_spell_was)
1477                   {   bozbar_spell:
1478                          "He's not that much of an animal.";
1479                       lobal_spell:
1480                          "His problem is listening, not hearing.";
1481                       caskly_spell:
1482                          "For a moment his hair seems to comb itself. 
1483                           Irritated, he ruffles it again, and the spell dies
1484                           an ignominious death.";
1485                       yomin_spell:
1486                           if (elephant has moved || barker_cube has moved)
1487                           "The barker's mind is a heap of grumbles about lost
1488                            prizes and scrawny Enchanters.";
1489                           if (self hasnt general)
1490                           {   give self general;
1491                              "~Hope that scrawny Enchanter doesn't pick 2306!~
1492                               thinks the barker (slowly).";
1493                           }
1494                           "~If that mark does win, hope it's only worthless
1495                            old 5802,~ ponders the barker.";
1496                   }
1497         ],
1498    has  animate scenery;
1499   
1500  Object -> prizes "prizes"
1501    with name "prize" "prizes",
1502         before [; "~Hands off those prizes!~"; ],
1503    has  scenery;
1504   
1505  Object -> pelephant "prize elephant"
1506    with name "prize" "elephant" "cuddly" "toy",
1507         description "Pink, cuddly, toy, elephant.  Says it all, really.",
1508         before [; Examine: ; default: "~Hands off those prizes!~"; ],
1509    has  scenery;
1510         
1511  Object -> pcube "prize cube"
1512    with name "prize" "featureless" "white" "cube",
1513         description "Wouldn't you like to win it?",
1514         before [; Examine: ; default: "~Hands off those prizes!~"; ],
1515    has  scenery;
1516   
1517  Object elephant "cuddly toy elephant"
1518    with name "cuddly" "toy" "elephant",
1519         description "Pink, cuddly, toy, elephant.  Says it all, really.",
1520         before
1521         [;  Cast: if (the_spell_was == bozbar_spell)
1522                       "Let me get this straight.  You, the enchanter who
1523                        defeated Krill, the head of the Borphee Guild
1524                        himself...  are attempting to grow wings on a pink
1525                        cuddly elephant?";
1526                   if (the_spell_was == yomin_spell) "Woolly.";
1527         ];
1528   
1529  FeaturelessCube barker_cube "cube";
1530   
1531  ! ----------------------------------------------------------------------------
1532  !   The spells in Helistar's grimoire
1533  ! ----------------------------------------------------------------------------
1534   
1535  Spell lleps_spell
1536    with name "lleps",
1537         purpose "reverse effect of memorised spell",
1538         magic
1539         [;   if (second==0 || second notin memory)
1540                  "The spell backfires, painfully.";
1541              if (second.number==100)
1542                  "You know that spell too well for your mind to be able
1543                   to accept the change.";
1544              if (second has reversed) give second ~reversed;
1545              else give second reversed;
1546              if (second==lleps_spell)
1547              {    memory.forget_spell(second);
1548                  "Your mind wrenches as the two lleps spells
1549                   cancel each other out, leaving only a sensation
1550                   quite like a hangover.";
1551              }
1552              print_ret "Your mind wrenches as ", (the) second,
1553                        " reverses itself.";
1554         ],
1555         unmagic
1556         [;   return self.magic();         !  The reverse of "lleps" is "lleps"
1557         ];
1558   
1559  Spell mortin_spell
1560    with name "mortin",
1561         purpose "cause immediate death of caster",
1562         magic
1563         [;   deadflag = true;
1564              "You really can't fault Helistar on this one.  Death is
1565               absolutely immediate, like a sudden blackout curtain...";
1566         ],
1567         unmagic
1568         [;   prepared_flag = true;
1569              "Nothing quite happens... and yet you feel enormously more
1570               confident as you go about this dangerous world.";
1571         ];
1572   
1573  ! ----------------------------------------------------------------------------
1574  !   Death and the Boneyard
1575  ! ----------------------------------------------------------------------------
1576   
1577  [ AfterLife;
1578    if (~~prepared_flag) rfalse;
1579    
1580    if (player in Balance_Room)
1581        "^^Your foresight in preparing a resurrection was wasted.  The
1582           tangled magic of the Balance Room coiled around your puny
1583           enchantment like a constricting serpent.";
1584   
1585    prepared_flag = false; deadflag = false; hearing_good = false;
1586   
1587    if (memory.capacity >= 2) memory.capacity--;
1588   
1589    while (child(player)~=0) move child(player) to parent(player);
1590    move players_book to player;
1591   
1592    print "^^With great foresight you prepared yourself for resurrection... 
1593             Your mind feels a little weaker, but at least you're alive.^";
1594    PlayerTo(Boneyard);
1595  ];
1596   
1597  Place Boneyard "Boneyard"
1598    with name "bones" "blades" "shoulder" "skulls",
1599         description
1600            "This is a room of bones.  Shoulder blades make up the floor,
1601             skulls the walls and leg-bones the door frames. The west exit
1602             leads into darkness, but the doorway to the north opens onto a
1603             seemingly normal scene.",
1604         n_to
1605         [;  if (scales.number ~= 0) return In_Cave;
1606             return Grasslands;
1607         ],
1608         w_to "Some magical force blocks your way, as though that doorway
1609               led into adventures from your past which you cannot rejoin now.",
1610         before
1611         [;  Examine, Search:
1612                 if (noun==w_obj) "You can make out nothing to the west.";
1613         ];
1614   
1615  Scroll -> worthless_scroll "worthless scroll"
1616    with initial "You are almost treading on a worthless scroll.",
1617         name "worthless";
1618   
1619  Spell -> -> filfre_spell
1620    with name "filfre",
1621         purpose "produce gratuitous fireworks",
1622         magic
1623         [;    if (self hasnt scored) { score++; give self scored; }
1624              "A brief shower of gratuitous fireworks spells out:
1625             ^^The masterly Enchanter trilogy was written by Marc Blank,
1626               Dave Lebling and Steve Meretzky.";
1627         ],
1628         unmagic
1629         [;   "A lengthy shower of artistically justified fireworks spells out:
1630             ^^The masterly Enchanter trilogy was written by Jane Austen,
1631               Emily Bronte and Edgar Allen Poe.";
1632         ];
1633   
1634  ! ----------------------------------------------------------------------------
1635  !   The Cubical Temple
1636  ! ----------------------------------------------------------------------------
1637   
1638  Place Track "Track, outside Temple"
1639    with description
1640            "This is the end of a long track winding through desolate hills,
1641             which runs back west up to the ridge.",
1642         before
1643         [;   Listen:
1644                  if (~~hearing_good) "The chanting is too quiet to make out.";
1645                  "The endlessly repeating threnody of the monks tells of
1646                   the legend of one who will some day enlighten their order,
1647                   and so be taken up to a higher plane.  He (or she,
1648                   presumably) is known as The Four-Cubed One.";
1649         ],
1650         w_to Up_Road, u_to Up_Road;
1651   
1652  Object -> Temple "cubical Temple"
1653    with name "temple" "cubical" "cube" "enormous",
1654         before
1655         [ i j;
1656           Enter: "The Temple is featureless and unbroken.  Perhaps the top
1657                   is open, because the sound must come from somewhere...
1658                   but you wouldn't bet on it.";
1659           Cast:   switch(the_spell_was)
1660                   {   rezrov_spell:
1661                          "The huge temple remains impassive at your relatively
1662                           puny enchantment.";
1663                       frotz_spell:
1664                           objectloop (i in player)
1665                               if (i ofclass FeaturelessCube) j++;
1666                           if (j==0)
1667                               "The temple shakes, but then is still again.";
1668                           if (j<4) "The temple shakes!  White light plays
1669                               over your hands and possessions, but then all is
1670                               still again.";
1671                           print "The temple shakes and white light bathes you. 
1672                               Smoothly it unfolds itself in a four-dimensional
1673                               way your senses can barely comprehend.  All you
1674                               know is that when it is over,
1675                               you find yourself in...^";
1676                           hearing_good = false; score=score+5;
1677                           PlayerTo(Balance_Room); rtrue;
1678                   }
1679         ],
1680         describe
1681         [;   print "^You stand outside an enormous temple in the shape of a
1682                     perfect, featureless white cube, four hundred feet on a
1683                     side.  From somewhere within you hear the ";
1684              if (hearing_good) print "bellowing noise";
1685              else print "tiny sound";
1686              " of the monks chanting.";
1687         ],
1688         description
1689             "It's much like every other gigantic temple in the shape of a
1690              featureless white cube you've ever seen.  No obvious way in.",
1691    has  static;
1692   
1693  ! ----------------------------------------------------------------------------
1694  !   Inside the Temple
1695  ! ----------------------------------------------------------------------------
1696   
1697  Place Balance_Room "Balance Room"
1698    with description
1699             "This seems to be the inside of a featureless white cube, forty
1700              feet on a side.  The air is stale and there is no exit.";
1701   
1702  Object -> balance_meter "image of the scales"
1703    with name "image" "scales" "of" "pair", article "the",
1704         initial "The image of a pair of scales hangs high in the air.  One
1705                  pan is much lower than the other.",
1706         before
1707         [; "It's only an image.";
1708         ],
1709    has  static;
1710   
1711  Object -> dusty_podium "dusty podium"
1712    with name "podium" "dusty" "cobwebs" "cobwebbed",
1713         initial "Far below the scales, in the centre of the ~floor~, is a
1714                  predictably-shaped podium, but it is so dusty and
1715                  cobwebbed that you can't see what it once was.",
1716         before
1717         [;  Cast: if (the_spell_was == caskly_spell)
1718                      "Nice try, but it is protected from enchantment.";
1719                  "However dusty it is, the podium is still protected from
1720                   casual enchantment.";
1721             Rub:  remove self; move balance_key to Balance_Room;
1722                   itobj = balance_key;
1723                  "No substitute for old-fashioned hard work, sometimes,
1724                   and after much patient (sneezy) scrubbing, the podium
1725                   appears in its true white glory.  Set into it are four
1726                   sockets, arranged in a two by two square.";
1727         ],
1728    has  static;
1729   
1730  Object balance_key "podium"
1731    with name "podium" "pedestal" "platform" "cubical",
1732         description "As predicted, it is cubical.",
1733         initial "Far below the scales, in the centre of the ~floor~, is a
1734                  predictably-shaped podium.  Set into it are four sockets,
1735                  arranged in a two by two square.",
1736    has  static supporter;
1737   
1738  Object -> sockets "two by two square"
1739    with name "square" "two" "by" "two",
1740         before
1741         [ i;  if (action~=##Examine || number_filled==0)
1742                "You'll have to say which socket you mean. 
1743                 (Let's call them ~top left~, ~bottom right~ and so on.)";
1744             objectloop (i in self)
1745             {   print (The) i;
1746                 if (child(i)==0) print " is empty.^";
1747                 else { print " contains ", (a) child(i), ".^"; }
1748             }
1749             rtrue;
1750         ],
1751    has  static;
1752   
1753  Class Socket
1754    with name "socket", article "the",
1755         before
1756         [;  Cast:  "The sockets are proof against magic.";
1757             Examine: print (The) self, ", cubical and slightly more
1758                      than four inches on a side, is decorated with ",
1759                      (string) self.description;
1760                      if (child(self) == nothing) ".";
1761                      print_ret ", and contains ", (a) child(self), ".";
1762             Receive: if (~~(noun ofclass FeaturelessCube))
1763                         "The socket rejects that.";
1764                      if (child(self) ~= nothing)
1765                         "There is already a cube in that socket.";
1766         ],
1767         after
1768         [;  LetGo:  number_filled--;
1769                     "With much struggle, you manage to pull the cube away.";
1770             Receive: number_filled++;
1771             if (number_filled==4)
1772             {   if (snakes_cube in bl_socket
1773                     && barker_cube in ul_socket
1774                     && cave_cube in br_socket
1775                     && eye_cube in ur_socket)
1776                 {   deadflag=2; score=score+5;
1777                    "As you place the final cube into the sockets, you feel
1778                     imbued with celestial wisdom (more so than usually). 
1779                     You find yourself growing to the height of the cube, so
1780                     that you pull the balances back level by hand, and then
1781                     you grow still further, out of the temple until it is but
1782                     a cube in your hand, and you are a giant towering over
1783                     the land.
1784                   ^^Then, of course, you wake up, glumly realising it's time
1785                     to go to your job at the new Borphee Laboratories and
1786                     all those Wheatstone bridge experiments.  But at least
1787                     you can dream about the old days.";
1788                 }
1789                "The sockets are all full now, but that doesn't mean
1790                 anything's happened.";
1791             }
1792            "The cube is a predictably perfect fit in the socket.";
1793         ],
1794    has  static container open;
1795   
1796  Socket -> bl_socket "bottom left socket"
1797    with name "bottom" "left" "serpent",
1798         description "a serpent";
1799   
1800  Socket -> ul_socket "top left socket"
1801    with name "top" "left" "bazaar",
1802         description "a scene in a bazaar";
1803   
1804  Socket -> br_socket "bottom right socket"
1805    with name "bottom" "right" "cave",
1806         description "an engraving of a rocky cave";
1807   
1808  Socket -> ur_socket "top right socket"
1809    with name "top" "right" "eye",
1810         description "an eye";
1811   
1812  ! ----------------------------------------------------------------------------
1813  !   That's all of the object definitions: just a little code and grammar left
1814  ! ----------------------------------------------------------------------------
1815   
1816  [ Initialise;
1817   
1818    location = Hut;
1819    move burin to player;
1820    move players_coin to player;
1821    move players_book to player;
1822   
1823    thedark.description =
1824        "It is pitch black.  You are likely to be eaten by a grue.";
1825    ! (In fact you are stone-cold certain not to be, but never mind.)
1826   
1827    players_book.learn_spell(gnusto_spell);
1828    players_book.learn_spell(frotz_spell);
1829    players_book.learn_spell(yomin_spell);
1830    players_book.learn_spell(rezrov_spell);
1831   
1832    helistars_book.learn_spell(frotz_spell);
1833    helistars_book.learn_spell(lleps_spell);
1834    helistars_book.learn_spell(mortin_spell);
1835   
1836    give gnusto_spell known_about;
1837   
1838   "^^^^^[Welcome to a short story called ~Balances~, one of the example
1839         games for the Inform design system. Some people may recognise the
1840         setting, but others might like to type ~how do spells work~ -
1841         the game responds to a few such questions.]
1842       ^^You feel a little confused as to how you got here.  Something
1843         to do with Helistar!  That's right, and how the world is so far
1844         off balance nowadays, after the Great Change.^^";
1845  ];
1846   
1847  [ PrintRank;
1848    print ", earning you the rank of ";
1849    if (score >= 50)  "Scientist.";
1850    if (score >= 40)  "Spellbreaker.";
1851    if (score >= 30)  "Sorcerer.";
1852    if (score >= 20)  "Enchanter.";
1853    if (score >= 10)  "novice Enchanter.";
1854    "lost dreamer.";
1855  ];
1856   
1857  [ DiagnoseSub;
1858    switch(memory.capacity)
1859    {   5: "You feel fine, and your memory is unimpaired.";
1860        4: "You feel shaky after your brush with death, but your mental
1861            faculties seem sound.";
1862        3: "For someone who has died twice, you're in reasonable shape.";
1863    }
1864    "How many times have you died now?  Your memory isn't what it was.";
1865  ];
1866   
1867  ! ----------------------------------------------------------------------------
1868  !   Grammar extensions needed by the spell-casting and cube-writing rules:
1869  ! ----------------------------------------------------------------------------
1870   
1871  Include "Grammar";
1872   
1873  [ AnyWord; from_char=0; to_char=0; the_named_word=wn++; return burin; ];
1874   
1875  [ QuotedText i j f;
1876     i = WordAddress(wn++); i=i-buffer;
1877     if (buffer->i=='"')
1878     {   for (j=i+1:j<=(buffer->1)+1:j++)
1879             if (buffer->j=='"') f=j;
1880         if (f==0) return -1;
1881         from_char = i+1; to_char=f-1;
1882         if (from_char>to_char) return -1;
1883         while (buffer+f > WordAddress(wn)) wn++; wn++;
1884         return burin;
1885     }
1886     return -1;
1887  ];
1888   
1889  Verb "write" "scribe"
1890                  * AnyWord "on" held              -> WriteOn
1891                  * QuotedText "on" held           -> WriteOn;
1892  Verb "copy"     * scope=CopyableSpell "to" noun  -> CopyTo;
1893  Verb "who" "what" "how"
1894                  * "do"  scope=Topic              -> Query
1895                  * "is"  scope=Topic              -> Query
1896                  * "was" scope=Topic              -> Query;
1897  Verb "spells" "memory"
1898                  *                                -> Spells;
1899  Verb "learn" "memorise" "memorize"
1900                  * scope=ReadableSpell            -> Learn;
1901  Extend "examine" first
1902                  * scope=ReadableSpell            -> Examine;
1903  Verb "c,cast"
1904                  *                                -> CastOne
1905                  * noun                           -> CastOne;
1906  Verb "cast"
1907                  * is_spell                       -> Cast
1908                  * is_spell "at" noun             -> Cast
1909                  * is_spell "on" noun             -> Cast;
1910  Verb "diagnose" "health"
1911                  *                                -> Diagnose;
1912   
1913  ! ----------------------------------------------------------------------------
1914  !   And one for the game itself.
1915  ! ----------------------------------------------------------------------------
1916   
1917  Verb "ride" "mount" "straddle"
1918                  * creature                       -> Ride
1919                  * noun                           -> Enter;
1920  Verb "flip" "toss" * is_coin                     -> TossCoin;
1921  ! ----------------------------------------------------------------------------


Last updated 23 June 2004. This web site has not been fully supported since April 2008. Information may be out of date. This page was originally managed by Graham Nelson (graham@gnelson.demon.co.uk) assisted by C Knight.