! ---------------------------------------------------------------------------- ! Balances: An exercise in parsing Release 2, 25/9/94 ! updated 6/10/94 ! ! This short story was written to demonstrate large-scale programming of ! the parser, and features multiple objects, complicated plurals, variable ! verbs, objects named by the player and questions. The spell-casting ! system is written in a "safe" way so that it could easily be transplanted, ! and is as "object-oriented" as seemed sensible to the author. ! ! A number of minor slips in Release 1 have been corrected, and those who ! reported them are gratefully thanked; and many suggestions have been ! added. A few others I've reluctantly declined in the interests of ! keeping the source code reasonably clean as an example program ! which compiles as a Standard as well as an Advanced game. ! Oh, and apologies to all those who think lleps of rezrov is vorzer. ! ! Needs Inform 5.4 or later to compile. ! ! This is slightly updated because the original did not properly work as ! an Advanced game (it was originally written as Standard), because it ! rather lazily got the first 2-byte word of the property list for "name" ! by the usage thing.name rather than properly looking up the 0th word ! in the array by (thing.&name)-->0 (thing.&name is the address of the ! list of data in the name property). Anyway, sorry. ! ---------------------------------------------------------------------------- Switches d; Constant Story "BALANCES"; Constant Headline "^An Interactive Short Story^\ Copyright (c) 1994 by Graham Nelson.^"; Release 2; Constant OBJECT_SCORE 5; Constant MAX_SCORE 51; Constant DEBUG; Include "Parser"; Include "VerbLib"; ! ---------------------------------------------------------------------------- ! The white featureless cubes from "Spellbreaker", which can be identified ! by being written on with the magic burin, so that their names are given ! by the player in the course of play ! ! A particularly witty thing to do is to give several of them the same name, ! or to frotz some of them to distinguish them from the others... ! And the game will have no problem with this. ! ---------------------------------------------------------------------------- Attribute is_cube; global cube_text_buffer data 8; global the_named_word = 0; Fake_Action Baptise; Class cube_class with number 0 0 0 0, description "A perfect white cube, four inches on a side.", parse_name [ i j flag; if (parser_action==##TheSame) { for (i=0:i<8:i++) if ((parser_one.&number)->i ~= (parser_two.&number)->i) return -2; return -1; } for (::i++) { j=NextWord(); flag=0; if (j=='cube' or 'white' || (j=='featureless' or 'blank' && ((self.&number)->0) == 0)) flag=1; if (j=='cubes') { flag=1; parser_action=##PluralFound; } if (flag==0 && ((self.&number)->0) ~= 0) { wn--; if (TextReader()==0) return i; for (j=0: j<8: j++) if ((self.&number)->j ~= cube_text_buffer->j) return i; flag=1; } if (flag==0) return i; } ], article "a", short_name [ i; if (((self.&number)->0) == 0) print "featureless white cube"; else { print "~"; while (((self.&number)->i) ~= 0) print char (self.&number)->i++; print "~ cube"; } rtrue; ], plural [; RunRoutines(self, short_name); print "s"; ], before [ i; Baptise: wn = the_named_word; if (TextReader()==0) return i; for (i=0: i<8: i++) (self.&number)->i = cube_text_buffer->i; self.article="the"; print "It is now called "; DefArt(self); "."; ], has is_cube scored; ! Copies word "wn" from what the player most recently typed, putting it as ! plain text into cube_text_buffer, returning false if no such word is there [ TextReader point i j len; for (i=0:i<8:i++) cube_text_buffer->i = 0; if (wn > parse->1) { wn++; rfalse; } i=wn*4+1; j=parse->i; point=j+buffer; len=parse->(i-1); for (i=0:ii = point->i; wn++; rtrue; ]; Object burin "magic burin" with name "magic" "magical" "burin" "pen", description "This is a magical burin, used for inscribing objects with words \ or runes of magical import. Such a burin also gives you the \ ability to write spell scrolls.", before [; WriteOn: if (second has is_cube) { if (second notin player) "To write on a cube, you need to be holding it first."; if (burin notin player) "You would need some powerful implement for that."; <>; } if (second has is_spell_book) "If a burin could write in a spell book, you wouldn't need \ the gnusto spell!"; if (second has is_scroll) "You cannot write just anything on the magic parchment of \ a scroll: you can only ~copy~ a spell to it."; ]; [ WriteOnSub; "Graffiti is banned."; ]; [ CopyToSub; if (burin notin player) "You need to be holding the burin to copy a spell."; if (second has is_spell_book) "If a burin could write in a spell book, you wouldn't need \ the gnusto spell!"; if (second hasnt is_scroll) "You can only copy spells to scrolls."; if (child(second)~=0) "The scroll is already full of incantation."; "The scroll is not blank, only illegible."; ]; ! ---------------------------------------------------------------------------- ! Now the whole spell-casting system ! ---------------------------------------------------------------------------- Attribute is_spell; Attribute is_scroll alias talkable; ! These are both Attribute is_spell_book alias female; ! dodges, in case compiling in old V3 Property long magic; [ SpellName obj; print_addr (obj.&name)-->0; ]; Class spell_class with name "spell" "spells", article "the", number 0, short_name [; SpellName(self); print " spell"; rtrue; ], before [; Examine: SpellName(self); print " spell: ", object self; rtrue; ], has is_spell; Object memory "memory" with capacity 5, number 1, before [ i j k; Examine: objectloop (i in self) if (i.number==100) j++; if (j>0) { print "The "; objectloop (i in self) if (i.number==100) { k++; SpellName(i); if (k==j-1) print " and "; if (k0) { print "the "; objectloop (i in self) if (i.number<100) { k++; PrintShortName(i); if (i.number==2) print " (twice)"; if (i.number==3) print " (thrice)"; if (i.number==4) print " (four times)"; if (i.number>=5) print " (many times)"; if (k==j-1) print " and "; if (k3) print " yet another time."; if (self.number <= self.capacity) { new_line; rtrue; } i=youngest(self); ; " You have so much buzzing around in your head, though, \ that it's likely something may have been forgotten \ in the shuffle."; Remove: if (second notin self || second.number==100) rtrue; if (self.number>0) self.number=self.number-1; second.number=second.number-1; if (second.number==0) remove second; rtrue; ]; Object gnusto_spell "copy a scroll into your spell book" memory class spell_class with name "gnusto", number 100, magic [ i a_book; if (second has is_spell_book) "Unlike scrolls, spell books are magically guarded against \ the 'theft' of their lore."; if (second==0 || second hasnt is_scroll) "Your spell fizzles vaguely out."; if (second notin player) "A gnusto spell would require close scrutiny of the scroll \ it is to copy: which you do not seem to be holding."; objectloop (i in player) if (i has is_spell_book) a_book=i; if (a_book==0) "Your spell fails, as you have no spell book."; i=child(second); if (i==0 || i hasnt is_spell) { print "Your spell fails, as "; DefArt(second); " is illegible."; } ; remove second; print "Your spell book begins to glow softly. Slowly, ornately, \ the words of "; DefArt(i); " are inscribed, \ glowing even more brightly then the book itself. \ The book's brightness fades, but the spell remains! \ However, the scroll on which it was written vanishes as \ the last word is copied."; ]; Class spell_book_class with magic 0, capacity 16, before [ p i; Open, Close: CDefArt(self); " is always open to the right place, but it \ is also always closed. This eliminates tedious leafing and \ hunting for spells. Many lives have been saved by this \ magical innovation."; Attack: print "When you are done, "; DefArt(self); " remains unmarred."; Learn: if (self.magic==0) "(This spell book has no pages.)"; p = self.magic; for (i=0:ii)~=0:i++) ; if (i==self.capacity) rtrue; p-->i = second; rtrue; ], after [ p i j; Examine: if (self.magic==0) "(This spell book has no pages.)"; p = self.magic; for (i=0:ii)~=0:i++) { j=p-->i; ; new_line; } rtrue; ], has is_spell_book; Class scroll_class with parse_name [ i j; j=-1; if (self has general) { if (child(self)~=0 && child(self) has is_spell) j=(child(self).&name)-->0; else j='illegible'; } while (NextWord()==j or 'scroll' or (self.&name)-->0) i++; return i; ], before [ i; Examine: i=child(self); give self general; if (i==0 || i hasnt is_spell) "The scroll has faded, and you cannot read it."; print "The scroll reads ~"; ; "~."; ], invent [; if (inventory_stage==2 && self has general) { if (child(self)==0 || child(self) hasnt is_spell) print " (which is illegible)"; else { print " (of "; DefArt(child(self)); print ")"; } } ], has is_scroll; [ ReadableSpell i j k; if (scope_stage==1) return 1; if (scope_stage==2) { objectloop (i in player) if (i has is_spell_book) { for (k=0:kk~=0:k++) { j=(i.magic)-->k; PlaceInScope(j); } } rtrue; } "No such spell is in any spell book you're holding."; ]; [ SpellsSub; ; ]; [ LearnSub; if (location==thedark) print "(The magic writing of the spells casts enough light \ that you can read them.)^"; ; ]; global the_spell_was = gnusto_spell; [ CastOneSub; ; ]; Property long unmagic; [ CastSub; the_spell_was = noun; ; if (noun has general) { give noun ~general; if (RunRoutines(noun,unmagic)~=0) rfalse; "Nothing happens."; } if (second~=0) { ResetVagueWords(second); ! Set "it", "him", "her" if (RunRoutines(second,before)~=0) rfalse; ! Run before routine(s) } if (RunRoutines(noun,magic)~=0) rfalse; "Nothing happens."; ]; [ InScope i; if (verb_word=='c#cast' or 'cast') objectloop (i in memory) PlaceInScope(i); rfalse; ]; [ ParserError; if (verb_word=='cast') "You have not learned such a spell."; rfalse; ]; [ UnknownVerb word i; objectloop (i in memory) if (word==(i.&name)-->0) { the_spell_was = i; return 'c#cast'; } rfalse; ]; [ PrintVerb v; if (v=='c#cast') { print "cast a spell at"; rtrue; } rfalse; ]; ! ---------------------------------------------------------------------------- ! And now, on with the story. First, some global variables: ! ---------------------------------------------------------------------------- global prepared_flag = 0; ! Prepared for resurrection? global hearing_good = 0; ! Sharp hearing? global number_filled = 0; ! Sockets in the temple filled global all_my_spells data 32; ! Array for contents of your spell book global helistars_spells data 32; ! ...and Helistar's ! ---------------------------------------------------------------------------- ! A "questions" verb. Thus, ! "who is my friend helistar" ! "what was the great change" ! and so on are recognised. ! ---------------------------------------------------------------------------- Object questions "qs"; [ QuerySub; print_paddr noun.description; new_line; ]; [ Topic i; if (scope_stage==1) return 0; if (scope_stage==2) { objectloop (i in questions) PlaceInScope(i); rtrue; } "At the moment, even the simplest questions confuse you."; ]; Object q1 "q1" questions with name "helistar" "my" "friend" "colleague", description "Helistar is your colleague, an Enchanter like you who has been much \ on your mind lately. She has been investigating some very dark \ magic indeed, and seems not to be around any more. You feel rather \ vague about the details."; Object q2 "q2" questions with name "magical" "magic" "burin", description "A burin is an engraving and writing tool."; Object q3 "q3" questions with name "change" "great", description "Something you had a lot to do with, but what exactly? No, it's gone."; Object q4 "q4" questions with name "cyclops", description "A one-eyed giant, usually hostile. (Don't they teach anything at \ adventurer school these days?)"; Object q5 "q5" questions with name "grue", description "The grue is a sinister, lurking presence in the dark places of the \ earth. Its favorite diet is adventurers, but its insatiable appetite \ is tempered by its fear of light. No grue has ever been seen by the \ light of day, and few have survived its fearsome jaws to \ tell the tale."; Object q6 "q6" questions with name "grimoire", description "According to Chambers English Dictionary, a grimoire is ~a magician's \ book for calling up spirits~."; ! ---------------------------------------------------------------------------- ! Some multiple objects, coins in fact, coded in deluxe fashion: ! ---------------------------------------------------------------------------- Attribute is_coin; Class coin_class with name "coin", description "A round unstamped disc, presumably part of the local \ currency.", parse_name [ i j w; if (parser_action==##TheSame) { if ((parser_one.&name)-->0 == (parser_two.&name)-->0) return -1; return -2; } w=(self.&name)-->0; for (::i++) { j=NextWord(); if (j=='coins') parser_action=##PluralFound; else if (j~='coin' or w) return i; } ], has is_coin; Class gold_coin_class class coin_class, with name "gold", plural "gold coins"; Class silver_coin_class class coin_class, with name "silver", plural "silver coins"; Class bronze_coin_class class coin_class, with name "bronze", plural "bronze coins"; Object coin1 "silver coin" class silver_coin_class; [ TossCoinSub; if (noun notin player) "You need to be holding the coin first."; move noun to parent(player); if (location==thedark) "You throw it away into the darkness."; if (random(20)==1) "You toss the coin, and it lands... on its edge, \ amazingly."; "You toss the coin, and it comes up... blank, since neither side is \ marked."; ]; ! ---------------------------------------------------------------------------- ! The player's spell book, and three initial spells (to go with gnusto): ! ---------------------------------------------------------------------------- Object spell_book "spell book" class spell_book_class, with name "spell" "book" "my" "spellbook", description "My Spell Book^"; Object frotz_spell "cause an object to give off light" class spell_class, with name "frotz", magic [; if (second==0) "There is a brief, blinding flash of light."; if (second has animate) "The spell, not designed for living creatures, goes sour."; if (parent(second)==compass) "The spell dissipates vaguely."; give second light; print "There is an almost blinding flash of light as "; DefArt(second); print " begins to glow! It slowly fades to a less painful level, but "; DefArt(second); " is now quite usable as a light source."; ], unmagic [; if (second==0) "There is a brief moment of deep darkness."; if (second has animate) "The spell, not designed for living creatures, goes sour."; if (parent(second)==compass) "The spell dissipates vaguely."; if (second hasnt light) { CDefArt(second); " isn't producing light as it is."; } give second ~light; print "A pool of darkness coagulates around "; DefArt(second); print " but slowly fades back to normality. Still, "; DefArt(second); " is no longer any kind of light source."; ]; Object rezrov_spell "open even locked or enchanted objects" class spell_class, with name "rezrov", magic [; if (second==0) "The world is open already."; if (second has animate) "It might be a boon to surgeons if it worked, but it doesn't."; if (second has open || second hasnt openable) "It doesn't need opening."; if (second hasnt locked) { give second open; CDefArt(second); " opens obediently. \ Like swatting a fly with a sledge hammer, if you ask me."; } give second open ~locked; print "Silently, "; DefArt(second); " swings open."; ], unmagic [; if (second==0) "The world is closed already."; if (second has animate) "Happily, that is unnecessary."; if (second has locked || second hasnt lockable) "It doesn't need locking."; give second ~open locked; print "Silently, "; DefArt(second); " swings shut and locks."; ]; Object yomin_spell "mind probe" class spell_class, with name "yomin", magic [; if (second==0 || second hasnt animate) "That must be either vegetable or mineral."; if (second==player) "You give yourself a mild headache."; print "You look into the rather ordinary thoughts of "; DefArt(second); "."; ], unmagic [; if (second==0 || second hasnt animate) "That must be either vegetable or mineral."; if (second==player) "You give yourself a mild headache."; CDefArt(second); " is rather shocked, for some reason."; ]; ! ---------------------------------------------------------------------------- ! The first scene: the Hut and its (rather easy) secret ! ---------------------------------------------------------------------------- Object Hut "Ramshackle Hut" with description "Until quite recently, someone lived here, you feel sure. \ Now the furniture is matchwood and \ the windows are glassless. Outside, it is a warm, sunny day, \ and grasslands extend to the low hills on the horizon.", out_to Grasslands, cant_go "There's only the one room: better go ~out~.", name "windows" "grasslands" "grass" "hills", has light; Nearby furniture "wooden furniture" with name "furniture" "broken" "wood" "wooden", before [; Examine, Search, LookUnder: self.before=0; score=score+5; move h_box to player; "Searching through the furniture, which is good for nothing \ but firewood now, you come across an old cedarwood box."; ], has scenery; Object h_box "cedarwood box" with name "cedar" "cedarwood" "wooden" "box", description "The box bears the calligraphed initial H." has container openable lockable locked; Nearby helistars_book "Helistar's grimoire" class spell_book_class, with name "grimoire" "helistar" "helistars", description "This must be the grimoire of dangerous spells kept by \ your irresponsible friend Helistar. Many pages are \ missing, but a few spells remain:^", has proper; ! ---------------------------------------------------------------------------- ! Grasslands and the valley ! ---------------------------------------------------------------------------- Object Grasslands "Grasslands, near Hut" with name "grasslands" "grass" "hut" "path", description "The grasslands sway over low hills in all directions: it is a \ peaceful wilderness, broken only by this hut and a faint path \ to the north.", in_to Hut, n_to Valley, cant_go "You wander around for a while but end up back at the hut." has light; Object Valley "Pocket Valley" with name "valley" "trail", description "A pleasant pocket valley in the grassy hills, through which a \ trail runs north-to-south.", n_to "The trail runs out to nothing, and you retreat for fear of \ getting so lost you couldn't find the hut again by nightfall.", cant_go "You wander around the pleasant valley, but are afraid to \ lose sight of the trail.", s_to Grasslands has light; [ RideSub; print "You can hardly ride "; IndefArt(noun); "."; ]; Nearby horse "horse" with short_name [; if (self has general) print "winged horse"; else print "chestnut horse"; rtrue; ], parse_name [ i j; if (self has general) j='winged'; else j=-1; while (NextWord()==j or 'horse' or 'chestnut') i++; return i; ], describe [; print "There is "; InDefArt(self); " here, munching on a pile of oats."; ], before [; Cast: if (the_spell_was == bozbar_spell) { give self general; "A pair of handsome brown wings suddenly appears on \ the horse's powerful shoulders. The horse turns in a \ complete circle, a look of puzzlement on his face."; } if (the_spell_was == yomin_spell) "He is mainly thinking about oats. Partly who you are \ and what you're up to, but mainly oats."; Enter: <>; Ride: if (horse hasnt general) "You ride around for a while, exercising the horse, but \ soon enough he tires of this and pointedly brings you \ back to the oats. Obligingly you dismount and he \ begins grazing again."; print "You begin to ride north. Then, slowly at first but with \ increasing sureness, the horse begins beating its powerful \ wings. You rise majestically through the air, sailing \ gracefully across a chasm where the hills fall away. \ The horse lands gently on the far side and deposits you, \ taking to the skies again.^"; PlayerTo(Edge); rtrue; ], has animate; Nearby oats "pile of oats" with name "oats" "pile" "of", before [; Examine, Search, LookUnder: self.before=0; move shiny_scroll to player; score=score+5; "Sifting through the oats, you find a shiny scroll! Lucky \ you got to it before the horse did."; Take: "What would you want with all those oats?"; ], has scenery; Object shiny_scroll "shiny scroll" class scroll_class, with name "shiny"; Object bozbar_spell "cause an animal to sprout wings" shiny_scroll class spell_class, with name "bozbar", magic [; if (second==0 || second hasnt animate) "The spell dies away in vain."; if (second==player) "Your elbows twitch, but there is no other effect."; print "For a moment, "; DefArt(second); " looks highly discomforted, but the moment passes."; ], unmagic [; if (second==0 || second hasnt animate) "The spell dies away in vain."; if (second==player) "What wings?"; if (second==horse && horse has general) { give horse ~general; "The Enchanter giveth, and the Enchanter taketh away. \ The horse looks disconsolate but returns to the oats."; } CDefArt(second); " has no wings to lose."; ]; ! ---------------------------------------------------------------------------- ! The Chasm and the snake ! ---------------------------------------------------------------------------- Object Edge "Edge of Chasm" with name "wide" "chasm" "road" "daffodils", description "The road ends suddenly at a wide chasm. The road leads upward \ to the north, and you can see it continuing on the southern side \ of the chasm.", u_to Up_Road, n_to Up_Road, cant_go "The chasm is too perilous to approach. The only safe way is \ up and to the north.", before [; Jump: deadflag=1; "You jump bravely into the chasm, and plunge... \ gracefully through the air. (It gets a bit less noble and \ airy after that.)"; ], has light; Nearby snake "hissing snake" with name "hissing" "snake", initial "Lying in a tight coil at the edge of the chasm is a hissing snake.", life [; "The snake hisses angrily!"; ], before [; Cast: if (the_spell_was == urbzig_spell) { remove self; snakes_cube.initial = "Beside a clump of daffodils is a featureless white cube."; "The snake is replaced by a clump of daffodils."; } if (the_spell_was == bozbar_spell) { deadflag=1; remove self; snakes_cube.initial = "A featureless cube rests where the snake took off from."; "The snake is transformed into a huge, winged serpent, \ a dragon which bellows and leaps out into the chasm, \ backwinging furiously... and knocking you over the \ edge quite by accident."; } if (the_spell_was == yomin_spell) "Horrid reptilian thoughts insinuate their way into you."; ], has animate; Nearby snakes_cube "cube" class cube_class, with initial "The snake appears to be curled around a featureless white cube.", before [; if (snake notin nothing) "The snake won't let you near that cube!"; ]; ! ---------------------------------------------------------------------------- ! The crest of the hill; Icarus the tortoise; the chewed scroll ! ---------------------------------------------------------------------------- Object Up_Road "Crest of Hill" with description "The road crosses the top of a ridge here, sloping downwards to \ the south and the northwest. A track diverges to east.", nw_to Cave_Mouth, s_to Edge, d_to Edge, e_to Track, has light; Nearby tortoise "tortoise" with name "tortoise" "turtle", initial "A tortoise ambles along the road, extremely slowly.", life [; "The tortoise (slowly) turns its neck to look at you (stupidly)."; ], before [; Cast: if (the_spell_was == urbzig_spell) "Just how safe do you want your surroundings to be?"; if (the_spell_was == bozbar_spell) { move chewed_scroll to parent(self); remove self; StartDaemon(self); score=score+5; "The tortoise seems to be incapable of expressing \ surprise, but is now soaring away high in the sky. \ Something rather grubby is left behind."; } if (the_spell_was == yomin_spell) "For a moment you think there is nothing there, as you \ chew absentmindedly on a leaf. But somewhere inside \ the tortoise is a sense of wonder at the amazing blue \ canopy of the sky."; ], daemon [ i; if (location ~= Up_Road or Track || random(6)~=1) rfalse; if (random(4)==1 && self hasnt general) { move feather to location; give self general; "^A tortoise-feather flutters to the ground before you!"; } i=random(3); if (i==1) print "^High in the sky,"; if (i==2) print "^Far above you,"; if (i==3) print "^Tiny in the blue sky,"; " a tortoise flaps across the sun."; ], has animate; Object torn_scroll "torn scroll" class scroll_class, with name "torn"; Nearby lobal_spell "sharpen hearing" class spell_class, with magic [; if (second==0 || second hasnt animate) "There is a loud bang in your ear, but no other effect."; if (second==player) { if (hearing_good==1) "There is no further effect."; hearing_good=1; StartTimer(self, 5); "Nothing happens, possibly because those butterflies on the \ other side of the hill keep distracting you."; } CDefArt(second); " is no doubt grateful for the gift of sharper hearing."; ], unmagic [; if (second==0 || second hasnt animate) "There is a brief silence, but no other effect."; if (second==player) { StopTimer(self); hearing_good=0; "Pardon?"; } CDefArt(second); " is no doubt grateful not to have to listen to you."; ], time_left 0, time_out [; if (hearing_good==0) rfalse; hearing_good=0; "^Those wretched butterflies finally shut up."; ], name "lobal"; Object chewed_scroll "chewed scroll" class scroll_class, with initial "It looks as if the tortoise was chewing something - once \ it might have been a scroll, but now it's chewed up like \ a lettuce leaf.", before [; Cast: if (the_spell_was == caskly_spell) { move torn_scroll to parent(self); remove self; score=score+5; "Before your eyes, the scroll begins to repair itself, \ failing only at the very last tear. Not quite perfect \ perhaps, but certainly a readable, if torn scroll."; } ], with name "chewed"; Object feather "tortoise feather" with name "tortoise" "feather", description "Possibly your rarest, and also least valuable, possession."; ! ---------------------------------------------------------------------------- ! The cave mouth and the perfect sapphire ! ---------------------------------------------------------------------------- Object Cave_Mouth "Cave Mouth" with name "gorse" "footpath" "cave" "mouth", description "This is a cave mouth, at one end of a road which winds southeast \ over rising ground. The entrance west to the caves is dark. \ Only a footpath runs further north, into gorse.", u_to Up_Road, se_to Up_Road, in_to Iron_Door, w_to Iron_Door, n_to Footpath has light; Nearby Iron_Door "iron door" with name "iron" "door" "heavy", description "It just looks like an ordinary heavy iron door.", door_dir [; if (location==Cave_Mouth) return w_to; return e_to; ], door_to [; if (location==Cave_Mouth) return In_Cave; return Cave_Mouth; ], describe [; if (self has open) "^The iron door stands open."; if (self hasnt locked) "^The iron door is unlocked but shut."; "A heavy iron door bars the cave mouth."; ], found_in In_Cave Cave_Mouth has static door openable locked lockable; ! Cf. T. S. Eliot, "Burnt Norton" II: Nearby sapphire "perfect sapphire" with name "perfect" "sapphire" "gemstone" "gem", initial "Clotted in the mud beside the door is a perfect sapphire.", before [; Examine: remove self; move caskly_spell to memory; ; caskly_spell.number=100; "As you gaze into the perfect blue of the sapphire, \ you feel your mind begin to reel. Unable to bear \ the naked sight of perfection, you look away, ashamed. \ As you do so, the sapphire cracks and wastes away to \ thin hot dust. But something remains, something in your \ mind..."; ]; Object caskly_spell "cause perfection" class spell_class, with name "caskly", magic [; if (second==0) "Trying to make everything perfect was a little \ too ambitious."; if (second==player) "Oh, don't be too hard on yourself."; if (second==helistars_book) "Your spell is not powerful enough to restore the lost pages."; CDefArt(second); " looks pretty perfect as is."; ]; ! ---------------------------------------------------------------------------- ! Inside the Cave, the powerful urbzig spell and its consequences ! ---------------------------------------------------------------------------- Object In_Cave "Inside Cave" with description "A wide but shallow cave not far inside the hill. There is no \ obvious exit, except for the way you came in.", out_to [; if (CoinsIn(left_pan)+CoinsIn(right_pan) < 6) "Something bars your way, and you hear \ the scales jangling militantly. You were trying to \ steal its coins!"; if (scales.number~=0) "Something bars your way, and you hear \ the scales jangle slightly with energy."; return Iron_Door; ], cant_go "The only way is back ~out~ through the iron door.", after [; Take: if (parent(noun)==left_pan or right_pan) { print "Taken from "; DefArt(parent(noun)); "."; } ]; Nearby cave_cube "cube" class cube_class, with initial "Balanced on a rock formation is a featureless white cube."; Nearby scales "pair of scales" with name "pair" "of" "scales" "pans", number 0, describe [; print "^A fair-sized pair of scales hangs from a bracket in the \ cave wall. "; if (self.number==0) "The scales are balanced."; if (self.number==1) "The left-hand side is higher."; "The right-hand side is higher."; ], before [; "There are left and right hand pans, which you should refer to \ individually."; ], has static supporter; Class pan_class with name "pan" "side" "tray", before [; Receive: if (noun has is_scroll || noun has is_coin) rfalse; if (noun==feather) rfalse; "The pans gleam with what almost seems greed, and somehow they \ contrive to nudge your hand past them with your worthless and \ boring item."; ], after [ i j d w1 w2; Receive, LetGo: i=scales.number; objectloop (j in left_pan) w1=w1 + WeightOf(j); objectloop (j in right_pan) w2=w2 + WeightOf(j); if (w1==w2) scales.number=0; if (w1 > w2) scales.number=-1; if (w1 < w2) scales.number=1; j=scales.number; d=(w2-w1)*(scales.number); if (j==i) rfalse; if (j==0) "The scales come into balance."; if (j==1) print "The left pan "; else print "The right pan "; if (d==1) "very slowly rises up."; "rises up."; ], has supporter scenery; [ WeightOf obj; if (obj==bronze_coin) return 2; if (obj has is_scroll || obj==feather) return 1; return 3; ]; [ CoinsIn obj i c; objectloop (i in obj) if (i has is_coin) c++; return c; ]; Nearby left_pan "left pan" class pan_class, with name "left"; Nearby right_pan "right pan" class pan_class, with name "right"; Object bronze_coin "bronze coin" left_pan class bronze_coin_class; Object coin3 "gold coin" left_pan class gold_coin_class; Object coin4 "gold coin" left_pan class gold_coin_class; Object coin5 "gold coin" right_pan class gold_coin_class; Object coin6 "silver coin" right_pan class silver_coin_class; Object coin7 "silver coin" right_pan class silver_coin_class; Object crumpled_scroll "crumpled scroll" left_pan class scroll_class, with name "crumpled"; Object urbzig_spell "turn a dangerous object into a harmless one" crumpled_scroll class spell_class, with name "urbzig", magic [; if (second==0) "The spell fizzles away."; if (second==player) "It's a matter of opinion, isn't it?"; if (second==helistars_book or mace || second has is_cube) { CDefArt(second); remove second; if (second==mace && cyclops in location) { remove cyclops; move eye_cube to location; " turns into a featureless white cube just as the cyclops \ was about to hit you with it. Mightily embarrassed \ by this, he drops the cube and runs off!"; } print " turns into a moth and flutters away.^"; rtrue; } print "Nothing obvious happens. Perhaps "; DefArt(second); " isn't so very dangerous after all."; ], unmagic [; if (second==0) "The spell fizzles away."; if (second==player) "It's a matter of opinion, isn't it?"; if (second has static || second has scenery) { print "Your spell is too weak for something quite as \ monumentally harmless as "; DefArt(second); "."; } if (second==helistars_book or snake || second has is_cube || second==cyclops or mace) "Nothing obvious happens."; if (second in player) { remove second; deadflag=1; "Suddenly, a tarantula races up your arm to your throat! \ Perhaps it was unwise to gizbru something you were \ actually holding."; } if (cyclops has general) "Nothing happens. Perhaps that's just as well, \ after the last time."; move cyclops to location; remove second; give cyclops general; StartTimer(cyclops, 5); CDefArt(second); " is replaced by a buck-toothed cyclops \ wielding a mace!"; ]; Object cyclops "buck-toothed cyclops" with name "buck" "toothed" "buck-toothed" "cyclops", initial "A huge buck-toothed cyclops menaces you, armed with a \ heavy mace!", before [; Cast: if (the_spell_was == bozbar_spell) "Does the term ~death wish~ mean anything to you?"; if (the_spell_was == urbzig_spell) "The cyclops bellows with glee as your spell has \ no effect. (After all, he wouldn't be ~dangerous~ if \ an urbzig spell worked on him, would he?)"; ], life [; "He roars incoherently, swinging the mace!"; ], time_left 0, time_out [; if (self notin location) { remove self; rtrue; } deadflag=1; remove mace; remove cyclops; "Feeling that he's given you quite long enough to explain why \ you made such a mess of his life, he swings the great mace \ maniacally down on you!"; ], each_turn [ i; i=random(4); if (i==1) "^The cyclops leaps and bellows!"; if (i==2) "^Whirling the mace, the cyclops jabbers at you incoherently."; if (i==3) "^The cyclops is losing patience (the appropriate cyclops \ word is untranslatable into English, but approximately means \ ~forbearance in not smashing all nearby skulls~)."; "^The cyclops jabs you with the mace, almost breaking your rib."; ], has animate transparent; Nearby mace "mace" with name "heavy" "mace" "axe", description "It looks much too heavy for you to even lift."; Nearby eye_cube "cube" class cube_class, with initial "A featureless white cube lies where the cyclops dropped it."; ! ---------------------------------------------------------------------------- ! The Footpath and the carpet ! ---------------------------------------------------------------------------- Object Footpath "Gorse Bushes" with description "The footpath from the cave mouth runs into dense, impenetrable \ gorse bushes. Perhaps it wasn't so much a footpath as a rill \ in the earth where roots wouldn't take; anyway, there's no way \ but back south.", s_to Cave_Mouth has light; Nearby carpet "beautiful red carpet" with name "beautiful" "magic" "red" "carpet", initial "Slung over one of the gorse bushes is a beautiful red carpet.", description "This is a carpet of unusual design. It is red, beautifully woven \ and bears a pattern of cubes.", before [ i; Receive: if (self notin location || self hasnt moved) "Not until the carpet's on the ground, you can't."; Ride: <>; Enter: if (self notin location || self hasnt moved) "Not until the carpet's on the ground, you can't."; if (location==Temple) "Mysteriously, the carpet rucks and pulls until you're \ thrown off. It settles back on the white floor with a \ contented sigh."; if (location==In_Cave) "The carpet rises suddenly, crashing into the roof of \ cave and throwing you back off again. Painfully."; if (location==Bazaar) i=Up_Road; else i=Bazaar; print "The carpet rises suddenly into the fluffy white \ clouds, and after a headlong journey deposits you...^"; move self to i; PlayerTo(i,1); move player to self; <>; Take: if (player in self) "Not while you're on it!"; for (i=child(self):i~=0:i=child(self)) { move i to location; print "(Dislodging "; DefArt(i); print ")^"; } ], has supporter enterable; ! ---------------------------------------------------------------------------- ! A Bazaar Lottery ! ---------------------------------------------------------------------------- Object Bazaar "Crowded Bazaar" with description "This is a crowded, noisy bazaar. Directly in front of you is \ a lottery! But the contemptuous-looking barker is doing a \ very poor trade: hardly anyone wants his first prize, the \ big cuddly toy elephant, or even his nineteenth prize, a \ featureless white cube.", each_turn [ i; i=random(4); if (i==1) "^~Roll up! Roll up! One silver piece for three goes!~"; if (i==2) "^~Come on, then! Just a silver coin gets you three!~"; if (i==3) "^~Think what you could win, all for one silver coin!~"; "^~This could be your lucky day!~"; ], before [; Learn: "~None of that!~ snaps the barker angrily, putting you off \ your study habits. He mutters about ~Enchanter cheats~, \ but under the circumstances you decide to let the insult \ pass."; ], cant_go "Everywhere, the crowds of jabbering natives block your way \ to all the good stalls. In fact, the only one you can get at is \ this dismal lottery.", has light; global last_called = 1; Nearby board "lottery board" with number 0, name "board" "lottery" "holes", description "There are a hundred holes each way, making, um, let's see, yes, \ ten thousand tickets in all. Still, there are nineteen prizes, \ so your odds must be, oh, well, not too awful anyway.", before [ i; if (action==##LetGo) { if (self.number==0) "The barker stabs you in the chest with \ his finger. ~That's a silver coin to you, bub!~"; for (i=taken_t1: i<=taken_t6: i++) if (last_called == i.number) "That ticket's already taken."; self.number=self.number - 1; for (i=taken_t1: i<=taken_t6: i++) if (parent(i)==0) { i.number = last_called; itobj = i; move i to player; give i moved proper; print "You take "; DefArt(i); " out of the board."; } } if (action~=##Examine) "The barker is burly, and won't let you \ tamper with the board."; ], initial "Behind the barker is a huge drilled board, and inside each little \ numbered hole is a rolled-up lottery ticket." has static container open; Class ticket_class with number -1, name "ticket", description [; if (self.number==2306) "It is labelled ~First Prize~!"; if (self.number==5802) "It is labelled ~Nineteenth Prize~."; "~You lose,~ says the ticket, with a smily face. ~Try again!~"; ], short_name [; if (self.number==-1) rfalse; print "lottery ticket ", self.number; rtrue; ], parse_name [ i j w; if (NextWord()~='lottery' or 'ticket') return 0; i=1; if (NextWord()=='lottery' or 'ticket') { i++; wn++; } w=TryNumber(wn-1); if (w==-1000) return i; if (w==0) return 0; if (self.number==-1) { for (j=taken_t1: j<=taken_t6: j++) if (w == j.number) rfalse; } else { if (self.number~=w) return 0; } i++; last_called = w; return i; ], before [; Examine: if (self in board) "That would be cheating!"; Cast: "~Get outta here, bub!~, the barker says, disgusted."; ]; Object ticket_in_board "rolled-up ticket" board class ticket_class; Object taken_t1 "t1" class ticket_class; Object taken_t2 "t2" class ticket_class; Object taken_t3 "t3" class ticket_class; Object taken_t4 "t4" class ticket_class; Object taken_t5 "t5" class ticket_class; Object taken_t6 "t6" class ticket_class; Object barker "barker" Bazaar with name "barker" "burly" "man", number 0, description "A boxer gone to seed who failed as a magician all down the \ coast, that'd be your guess.", life [; Attack, Kiss: "No way. He must weigh twice what you do."; Ask: if (second=='prize' or 'prizes') "~Just one silver coin and a prize could be yours!~"; if (second=='white' or 'featureless' or 'cube') "He blows the dust off it. ~Genuine antique, that.~"; if (second=='elephant' or 'toy' or 'cuddly') "~Good quality merchandise,~ he says, in a way that \ suggests he can only spell one of those three words."; if (second=='ticket' or 'tickets' or 'lottery') "~Three tickets for one silver coin!~"; "~Just play the game, bub.~"; Order, Answer: "The barker glowers at you."; Give: if ((noun.&name)-->0 == 'ticket') { remove noun; if (noun.number==2306) { move elephant to player; give elephant moved; Bazaar.description = "This is a crowded, noisy bazaar. Directly in front of you is \ the lottery!"; "With very bad grace, the barker shoves the \ cuddly toy elephant into your arms."; } if (noun.number==5802) { move barker_cube to player; give barker_cube moved; Bazaar.description = "This is a crowded, noisy bazaar. Directly in front of you is \ the lottery!"; score=score+5; "With concealed relief, the barker shoves the \ featureless white cube into your hands."; } "~Bad luck! You lose!~"; } if (self.number==2) "~You've had enough goes already!~ he \ growls. No wonder trade is bad."; if (noun hasnt is_coin) "~What do you call that? One silver \ coin to play!~"; if ((noun.&name)-->0 == 'bronze') "~Bronze! Not a chance, sunshine.~"; remove noun; board.number = board.number + 3; self.number=self.number+1; if ((noun.&name)-->0 == 'gold') "Gleefully the barker snatches the gold coin. ~Sorry \ bub, no change. Business is slack today!~"; "Grudgingly the barker takes the silver coin and stands \ back to let you at the board, arms folded."; ], before [; Cast: if (the_spell_was == bozbar_spell) "He's not that much of an animal."; if (the_spell_was == lobal_spell) "His problem is listening, not hearing."; if (the_spell_was == caskly_spell) "For a moment his hair seems to comb itself. Irritated, \ he ruffles it again, and the spell dies an ignominious \ death."; if (the_spell_was == yomin_spell) { if (elephant has moved || barker_cube has moved) "The barker's mind is a heap of grumbles about lost \ prizes and scrawny Enchanters."; if (self hasnt general) { give self general; "~Hope that scrawny Enchanter doesn't pick 2306!~ \ thinks the barker (slowly)."; } "~If that mark does win, hope it's only worthless \ old 5802,~ ponders the barker."; } ], has animate scenery; Object prizes "prizes" Bazaar with name "prize" "prizes", before [; "~Hands off those prizes!~"; ], has scenery; Object elephant "cuddly toy elephant" with name "cuddly" "toy" "elephant", description "Pink, cuddly, toy, elephant. Says it all, really.", before [; Cast: if (the_spell_was == bozbar_spell) "Let me get this straight. You, the enchanter who \ defeated Krill, the head of the Borphee Guild \ himself... are attempting to grow wings on a pink \ cuddly elephant?"; ]; Object barker_cube "cube" class cube_class; ! ---------------------------------------------------------------------------- ! The spells in Helistar's grimoire ! ---------------------------------------------------------------------------- Object lleps_spell "reverse effect of memorised spell" class spell_class, with name "lleps", magic [; if (second==0 || parent(second)~=memory) "The spell backfires, painfully."; if (second.number==100) "You know that spell too well for your mind to be able \ to accept the change."; if (second has general) give second ~general; else give second general; print "Your mind wrenches as "; DefArt(second); " reverses itself."; ], unmagic [; RunRoutines(self,magic); rtrue; ]; Object mortin_spell "cause immediate death of caster" class spell_class, with name "mortin", magic [; deadflag=1; "You really can't fault Helistar on this one. Death is \ absolutely immediate, like a sudden blackout curtain..."; ], unmagic [; prepared_flag=1; "Nothing quite happens... and yet you feel enormously more \ confident as you go about this dangerous world."; ]; ! ---------------------------------------------------------------------------- ! Death and the Boneyard ! ---------------------------------------------------------------------------- [ AfterLife i; if (prepared_flag==0) rfalse; if (parent(player)==Balance_Room) "^^Your foresight in preparing a resurrection was wasted. The \ tangled magic of the Balance Room coiled around your puny \ enchantment like a constricting serpent."; prepared_flag=0; deadflag=0; hearing_good=0; i=memory.capacity; if (i>1) memory.capacity = i-1; i=parent(player); while (child(player)~=0) move child(player) to i; move spell_book to player; print "^^With great foresight you prepared yourself for resurrection... \ Your mind feels a little weaker, but at least you're alive.^"; PlayerTo(Boneyard); ]; Object Boneyard "Boneyard" with name "bones" "blades" "shoulder" "skulls", description "This is a room of bones. Shoulder blades make up the floor, \ skulls the walls and leg-bones the door frames. The west exit \ leads into darkness, but the doorway to the north opens onto a \ seemingly normal grassy scene.", n_to Grasslands, w_to "Some magical force blocks your way.", before [; Examine, Search: if (noun==w_obj) "You can make out nothing to the west."; ], has light; Nearby worthless_scroll "worthless scroll" class scroll_class, with initial "You are almost treading on a worthless scroll.", name "worthless"; Object filfre_spell "produce gratuitous fireworks" worthless_scroll class spell_class, with name "filfre", magic [; if (self hasnt scored) { score++; give self scored; } "A brief shower of gratuitous fireworks spells out:^^\ The masterly Enchanter trilogy was written by Marc Blank, \ David Lebling and Steve Meretzky."; ], unmagic [; "A lengthy shower of artistically justified fireworks spells out:^^\ The masterly Enchanter trilogy was written by Jane Austen, \ Emily Bronte and Edgar Allen Poe."; ]; ! ---------------------------------------------------------------------------- ! The Cubical Temple ! ---------------------------------------------------------------------------- Object Track "Track, outside Temple" with description "This is the end of a long track winding through desolate hills, \ which runs back west up to the ridge.", before [; Listen: if (hearing_good==0) "The chanting is too quiet to make out."; "The endlessly repeating threnody of the monks tells of \ the legend of one who will some day enlighten their order, \ and so be taken up to a higher plane. He (or she, \ presumably) is known as The Four-Cubed One."; ], w_to Up_Road, u_to Up_Road has light; Nearby Temple "cubical Temple" with name "temple" "cubical" "cube" "enormous", before [ i j; Enter: "The Temple is featureless and unbroken. Perhaps the top \ is open, because the sound must come from somewhere... \ but you wouldn't bet on it."; Cast: if (the_spell_was == rezrov_spell) "The huge temple remains impassive at your relatively \ puny enchantment."; if (the_spell_was == frotz_spell) { objectloop (i in player) if (i has is_cube) j++; if (j==0) "The temple shakes, but then is still again."; if (j<4) "The temple shakes! White light plays \ over your hands and possessions, but then all is \ still again."; print "The temple shakes and white light bathes you. \ Smoothly it unfolds itself in a four-dimensional \ way your senses can barely comprehend. All you \ know is that when it is over, \ you find yourself in...^"; hearing_good=0; score=score+5; PlayerTo(Balance_Room); rtrue; } ], describe [; print "^You stand outside an enormous temple in the shape of a \ perfect, featureless white cube, four hundred feet on a \ side. From somewhere within you hear the "; if (hearing_good==1) print "bellowing noise"; else print "tiny sound"; " of the monks chanting."; ], description "It's much like every other gigantic temple in the shape of a \ featureless white cube you've ever seen. No obvious way in.", has static; ! ---------------------------------------------------------------------------- ! Inside the Temple ! ---------------------------------------------------------------------------- Object Balance_Room "Balance Room" with description "This seems to be the inside of a featureless white cube, forty \ feet on a side. The air is stale and there is no exit.", has light; Nearby balance_meter "image of the scales" with name "image" "scales" "of" "pair", article "the", initial "The image of a pair of scales hangs high in the air. One \ pan is much lower than the other.", before [; "It's only an image."; ], has static; Nearby dusty_podium "dusty podium" with name "podium" "dusty" "cobwebs" "cobwebbed", initial "Far below the scales, in the centre of the ~floor~, is a \ predictably-shaped podium, but it is so dusty and \ cobwebbed that you can't see what it once was.", before [; Cast: if (the_spell_was == caskly_spell) "Nice try, but it is protected from enchantment."; "However dusty it is, the podium is still protected from \ casual enchantment."; Rub: remove self; move balance_key to Balance_Room; itobj = balance_key; "No substitute for old-fashioned hard work, sometimes, \ and after much patient (sneezy) scrubbing, the podium \ appears in its true white glory. Set into it are four \ sockets, arranged in a two by two square."; ], has static; Object balance_key "podium" with name "podium" "pedestal" "platform" "cubical", initial "Far below the scales, in the centre of the ~floor~, is a \ predictably-shaped podium. Set into it are four sockets, \ arranged in a two by two square.", has static supporter; Nearby sockets "two by two square" with name "square" "two" "by" "two", before [ i; if (action~=##Examine || number_filled==0) "You'll have to say which socket you mean. \ (Let's call them ~top left~, ~bottom right~ and so on.)"; objectloop (i in self) { CDefArt(i); if (child(i)==0) print " is empty.^"; else { print " contains "; IndefArt(child(i)); print ".^"; } } rtrue; ], has static; Class socket_class with name "socket", article "the", before [; Cast: "The sockets are proof against magic."; Examine: CDefArt(self); print ", cubical and slightly more \ than four inches on a side, is decorated with "; print_paddr self.description; if (child(self)==0) "."; print ", and contains "; IndefArt(child(self)); "."; Receive: if (noun hasnt is_cube) "The socket rejects that."; if (child(self)~=0) "There is already a cube in that socket."; ], after [; LetGo: number_filled--; "With much struggle, you manage to pull the cube away."; Receive: number_filled++; if (number_filled==4) { if (snakes_cube in bl_socket && barker_cube in ul_socket && cave_cube in br_socket && eye_cube in ur_socket) { deadflag=2; score=score+5; "As you place the final cube into the sockets, you feel \ imbued with celestial wisdom (more so than usually). \ You find yourself growing to the height of the cube, so \ that you pull the balances back level by hand, and then \ you grow still further, out of the temple until it is but \ a cube in your hand, and you are a giant towering over \ the land.^^\ Then, of course, you wake up, glumly realising it's time \ to go to your job at the new Borphee Laboratories and \ all those Wheatstone bridge experiments. But at least \ you can dream about the old days."; } "The sockets are all full now, but that doesn't mean \ anything's happened."; } "The cube is a predictably perfect fit in the socket."; ], has static container open; Nearby bl_socket "bottom left socket" class socket_class with name "bottom" "left" "serpent", description "a serpent"; Nearby ul_socket "top left socket" class socket_class with name "top" "left" "bazaar", description "a scene in a bazaar"; Nearby br_socket "bottom right socket" class socket_class with name "bottom" "right" "cave", description "an engraving of a rocky cave"; Nearby ur_socket "top right socket" class socket_class with name "top" "right" "eye", description "an eye"; ! ---------------------------------------------------------------------------- ! That's all of the object definitions: just a little code and grammar left ! ---------------------------------------------------------------------------- [ Initialise; location = Hut; move burin to player; move coin1 to player; move spell_book to player; thedark.description = "It is pitch black. You are likely to be eaten by a grue."; ! (In fact you are stone-cold certain not to be, but never mind.) spell_book.magic = all_my_spells; ; ; ; ; helistars_book.magic = helistars_spells; ; ; ; print "^^^^^This transcript is not from the Enchanter trilogy, but it \ does show most of the usual things you can do in those stories...^^"; print "You feel a little confused as to how you got here. Something \ to do with Helistar! That's right, and how the world is so far \ off balance nowadays, after the Great Change.^^"; ]; [ PrintRank; print ", earning you the rank of "; if (score >= 50) "Scientist."; if (score >= 40) "Spellbreaker."; if (score >= 30) "Sorcerer."; if (score >= 20) "Enchanter."; if (score >= 10) "novice Enchanter."; "lost dreamer."; ]; [ DiagnoseSub i; i=memory.capacity; if (i==5) "You feel fine, and your memory is unimpaired."; if (i==4) "You feel shaky after your brush with death, but your mental \ faculties seem sound."; if (i==3) "For someone who has died twice, you're in reasonable shape."; "How many times have you died now? Your memory isn't what it was."; ]; ! ---------------------------------------------------------------------------- ! Grammar extensions needed by the spell-casting and cube-writing rules: ! ---------------------------------------------------------------------------- Include "Grammar"; [ AnyWord; the_named_word=wn++; return burin; ]; Verb "write" "scribe" * AnyWord "on" noun -> WriteOn; Verb "copy" * scope=ReadableSpell "to" noun -> CopyTo; Verb "who" "what" * "is" scope=Topic -> Query * "was" scope=Topic -> Query; Verb "spells" "memory" * -> Spells; Verb "learn" "memorise" "memorize" * scope=ReadableSpell -> Learn; Extend "examine" first * scope=ReadableSpell -> Examine; Verb "c#cast" * -> CastOne * noun -> CastOne; Verb "cast" * is_spell -> Cast * is_spell "at" noun -> Cast * is_spell "on" noun -> Cast; Verb "diagnose" "health" * -> Diagnose; ! ---------------------------------------------------------------------------- ! And one for the game itself. ! ---------------------------------------------------------------------------- Verb "ride" "mount" "straddle" * creature -> Ride * noun -> Enter; Verb "flip" "toss" * is_coin -> TossCoin; end;