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 spro