! ---------------------------------------------------------------------------- ! Balances: An exercise in parsing 25/ 9/94 ! updated 6/10/94 ! modernised 11/12/95 ! ! 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. ! ! Release 3 contains many small improvements, especially in replying well ! to incorrect input. ! ! Advert: Michael Phillips has written an ingenious and elegant "adaptive ! hints" library for Inform, and used "Balances" as an example game to ! show how to program such hints. I recommend taking a look. ! ! Needs Inform 5.5, library 5/12 or later to compile. ! ---------------------------------------------------------------------------- Release 3; Serial "951220"; Switches dv5x; Constant Story "BALANCES"; Constant Headline "^An Interactive Short Story^\ Copyright (c) 1994, 1995 by Graham Nelson.^"; Constant OBJECT_SCORE 5; Constant MAX_SCORE 51; Replace LowKey_Menu; Replace DoMenu; 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; Array cube_text_buffer -> 8; Global the_named_word = 0; Global from_char; Global to_char; 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)==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(1)==0) return i; for (i=0: i<8: i++) (self.&number)->i = cube_text_buffer->i; self.article="the"; print_ret "It is now called ", (the) 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 flag point i j len; if (flag==1 && from_char~=to_char) { for (i=from_char, j=0:i<=to_char && j<7:i++) { cube_text_buffer->j = buffer->i; if (buffer->i ~= ' ' or ',' or '.') j++; } for (:j<8:j++) cube_text_buffer->j = 0; from_char=0; to_char=0; rtrue; } 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) "Writing on a cube is such a fiddly process that you \ need to be holding it in your hand 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 known_about; Attribute is_scroll; Attribute is_spell_book; Property magic; Fake_Action SayName; [ SpellName obj; print (address) (obj.&name)-->0; ]; Class spell_class with name "spell" "spells", article "the", number 0, short_name [; SpellName(self); print " spell"; give self known_about; rtrue; ], before [; SayName: SpellName(self); print " spell: ", object self; give self known_about; rtrue; Examine: ; "."; ], 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_ret "Your spell fails, as ", (the) second, " is illegible."; } ; remove second; print_ret "Your spell book begins to glow softly. Slowly, ornately, \ the words of ", (the) 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: print_ret (The) 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_ret "When you are done, ", (the) 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; ; } rtrue; ], has is_spell_book; Class scroll_class with parse_name [ i j k; j=-1; if (self has general) { if (child(self)~=0 && child(self) has is_spell) j=(child(self).&name)-->0; else j='illegible'; } for (::) { k=NextWord(); if (k=='scrolls') parser_action=##PluralFound; if ((k=='scrolls' or 'scroll' or j) || k==(self.&name)-->0) i++; else 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 ", (the) child(self), ")"; } } ], has is_scroll; [ ReadableSpell i j k; if (scope_stage==1) { if (action_to_be==##Examine) rfalse; rtrue; } 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 need for scope_stage 3 (the error stage), because our ! ParserError routine handles that case instead ]; [ CopyableSpell 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); } } rfalse; } ! No need for scope_stage 3 (the error stage), because our ! ParserError routine handles that case instead ]; [ 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 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 x i flag vb; if (etype==VERB_PE or ASKSCOPE_PE) { if (etype==ASKSCOPE_PE) { if (verb_word=='cast') vb=1; if (verb_word=='learn' or 'memorise' or 'memorize') vb=2; if (verb_word=='copy') vb=3; if (vb==0) { etype=CANTSEE_PE; rfalse; } } wn=verb_wordnum; if (vb~=0) wn++; x=NextWordStopped(); for (i=player+1:i<=top_object:i++) if (i has is_spell && Refers(i,x)==1 && i has known_about) flag=1; if (flag==1) { if (vb==0 or 1) "You haven't got that spell committed to memory. [Type ~spells~ \ to see what you do remember.]"; if (vb==2) "Your training is such that you can only memorise such a spell \ with the aid of a spell book containing it."; if (vb==3) "You have no text of that spell to copy."; } if (vb==1) "You haven't learned that spell, if indeed it is a spell."; if (vb==2 or 3) "You haven't access to that spell, if indeed it is a spell."; } rfalse; ]; [ ChooseObjects obj code; if (code<2) rfalse; if (action_to_be==##WriteOn && obj in player) return 9; return 0; ]; [ 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 -> 32; ! Array for contents of your spell book Global helistars_spells -> 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_ret (string) noun.description; ]; [ 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_ret "There is an almost blinding flash of light as ", (the) second, " begins to glow! It slowly fades to a less painful level, but ", (the) 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) print_ret (The) second, " isn't producing light as it is."; give second ~light; print_ret "A pool of darkness coagulates around ", (the) second, " but slowly fades back to normality. Still, ", (the) 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; print_ret (The) second, " opens obediently. \ Like swatting a fly with a sledge hammer, if you ask me."; } give second open ~locked; print "Silently, ", (the) second, " swings open. "; if (second has container) <>; new_line; rtrue; ], 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, ", (the) 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_ret "You look into the rather ordinary thoughts of ", (the) second, "."; ], unmagic [; if (second==0 || second hasnt animate) "That must be either vegetable or mineral."; if (second==player) "You give yourself a mild headache."; print_ret (The) 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, w_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, \ which you pick up for a closer look."; ], 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, e_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_ret "You can hardly ride ", (a) 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_ret "There is ", (a) 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=NULL; move shiny_scroll to player; score=score+5; itobj=shiny_scroll; "Sifting through the oats, you find a shiny scroll! Lucky \ you got to it before the horse did. As you turn it over \ in your hands, it seems undamaged."; 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_ret "For a moment, ", (the) 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."; } print_ret (The) second, " has no wings to lose."; ]; ! ---------------------------------------------------------------------------- ! The Chasm and the snake ! ---------------------------------------------------------------------------- Object Edge "Edge of Chasm" with name "wide" "chasm" "road" "daffodils" "clump", 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.", description "It has some V-markings, some scaly parts, colours from grey to \ reddish-brown. Is that any help?", life [; "The snake hisses angrily!"; ], before [; Cast: switch(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."; 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."; yomin_spell: "Horrid reptilian thoughts insinuate their way into you."; } Take, Remove: "The slipperiness of its skin is only one of many reasons \ why this is ill-advised."; ], 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: switch(the_spell_was) { urbzig_spell: "Just how safe do you want your surroundings to be?"; 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."; 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."; } Take, Remove: "Your parents always warned you not to pick up casual \ acquaintances met on the road."; ], 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); switch(i) { 1: print "^High in the sky,"; 2: print "^Far above you,"; 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."; } print_ret (The) 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?"; } print_ret (The) 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 lies there, \ 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 a dark \ tunnel, and 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: ! (but see also Mallarme's sonnet from which Eliot borrowed the image) 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."; print_ret (The) 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; ], e_to [; return RunRoutines(self,out_to); ], cant_go "The only way is back ~out~ through the iron door.", after [; Take: if (parent(noun)==left_pan or right_pan) print_ret "Taken from ", (the) 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_ret "Nothing obvious happens. Perhaps ", (the) 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_ret "Your spell is too weak for something quite as \ monumentally harmless as ", (the) 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); print_ret (The) 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==Balance_Room) "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 \ the 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 ", (the) i, ")^"; } ], has supporter enterable; ! ---------------------------------------------------------------------------- ! A Bazaar Lottery ! ---------------------------------------------------------------------------- Attribute is_ticket; 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 [; switch(random(4)) { 1: "^~Roll up! Roll up! One silver piece for three goes!~"; 2: "^~Come on, then! Just a silver coin gets you three!~"; 3: "^~Think what you could win, all for one silver coin!~"; 4: "^~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; Global explicit_flag = 0; Global tickets_taken = 0; 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; LetGo: if (self.number==0) "The barker stabs you in the chest with \ his finger. ~That's a silver coin to you, bub!~"; tickets_taken++; .RandomTicket; if (explicit_flag==0) last_called=random(10000); for (i=taken_t1: i<=taken_t6: i++) if (last_called == i.number) { if (explicit_flag==0) jump RandomTicket; "That ticket's already taken."; } self.number=self.number - 1; for (i=taken_t1: i<=taken_t6: i++) if (i hasnt moved) { i.number = last_called; itobj = i; move i to player; give i moved proper; if (explicit_flag==0) print "Randomly picking from the ", 10001-tickets_taken, " numbered holes with tickets in, you "; else print "You "; print_ret "take ", (the) i, " out of the board."; } Examine: ; Receive: if (noun has is_ticket) "~No changes of mind, that's your ticket now! Give it to \ me if you want to play it.~"; <>; default: "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; i=0; if (NextWord()=='lottery') i++; else wn--; if (NextWord()=='tickets') { parser_action=##PluralFound; return i+1; } else wn--; if (NextWord()~='ticket') return 0; if (self==ticket_in_board) explicit_flag=0; i++; w=TryNumber(wn); 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 && TestScope(j) ~= 0) rfalse; } else { if (self.number~=w) return 0; } if (self==ticket_in_board) { explicit_flag=1; last_called = w; } i++; return i; ], before [; Examine: if (self in board) "It would be cheating to see what's written on the curled up \ tickets still in the board."; Cast: "~Get outta here, bub!~, the barker says, disgusted."; ], has is_ticket; Object ticket_in_board "rolled-up ticket from the board" board class ticket_class with article "a"; 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: switch(second) { 'prize', 'prizes': "~Just one silver coin and a prize could be yours!~"; 'white', 'featureless', 'cube': "He blows the dust off it. ~Genuine antique, that.~"; 'elephant', 'toy', 'cuddly': "~Good quality merchandise,~ he says, in a way that \ suggests he can only spell one of those three words."; 'ticket', 'tickets', 'lottery': "~Three tickets for one silver coin!~"; default: "~Just play the game, bub.~"; } Order, Answer: "The barker glowers at you."; Give: if (noun has is_ticket) { remove noun; if (noun.number==2306) { move elephant to player; give elephant moved; remove pelephant; 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; remove pcube; 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: switch(the_spell_was) { bozbar_spell: "He's not that much of an animal."; lobal_spell: "His problem is listening, not hearing."; caskly_spell: "For a moment his hair seems to comb itself. \ Irritated, he ruffles it again, and the spell dies \ an ignominious death."; 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 pelephant "prize elephant" Bazaar with name "prize" "elephant" "cuddly" "toy", description "Pink, cuddly, toy, elephant. Says it all, really.", before [; Examine: ; default: "~Hands off those prizes!~"; ], has scenery; Object pcube "prize cube" Bazaar with name "prize" "featureless" "white" "cube", description "Wouldn't you like to win it?", before [; Examine: ; default: "~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?"; if (the_spell_was == yomin_spell) "Woolly."; ]; 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; if (second==lleps_spell) { ; "Your mind wrenches as the two lleps spells \ cancel each other out, leaving only a sensation \ quite like a hangover."; } print_ret "Your mind wrenches as ", (the) 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, as though that doorway \ led into adventures from your past which you cannot rejoin now.", 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, \ Dave 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: switch(the_spell_was) { rezrov_spell: "The huge temple remains impassive at your relatively \ puny enchantment."; 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", description "As predicted, it is 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) { print (The) i; if (child(i)==0) print " is empty.^"; else { print " contains ", (a) child(i), ".^"; } } rtrue; ], has static; Class socket_class with name "socket", article "the", before [; Cast: "The sockets are proof against magic."; Examine: print (The) self, ", cubical and slightly more \ than four inches on a side, is decorated with ", (string) self.description; if (child(self)==0) "."; print_ret ", and contains ", (a) 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; ; ; ; ; give gnusto_spell known_about; 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 "NewMenu"; Constant SHOWSOLVEDTAG; Constant HINTDEBUGVERBS; Include "AdHints"; Include "ah_bal"; Include "Grammar"; [ AnyWord; from_char=0; to_char=0; the_named_word=wn++; return burin; ]; [ QuotedText i j f; i = WordAddress(wn++); i=i-buffer; if (buffer->i=='"') { for (j=i+1:j<=(buffer->1)+1:j++) if (buffer->j=='"') f=j; if (f==0) return -1; from_char = i+1; to_char=f-1; if (from_char>to_char) return -1; while (buffer+f > WordAddress(wn)) wn++; wn++; return burin; } return -1; ]; Verb "write" "scribe" * AnyWord "on" held -> WriteOn * QuotedText "on" held -> WriteOn; Verb "copy" * scope=CopyableSpell "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;