diff -c origlib/English.h lib610/English.h
*** origlib/English.h	Sat Nov 13 11:29:52 1999
--- lib610/English.h	Sat Sep  2 11:04:51 2000
***************
*** 288,294 ****
--- 288,298 ----
  Constant ARE__TX      = " are";
  Constant IS2__TX      = "is ";
  Constant ARE2__TX     = "are ";
+ #ifdef AMERICAN_COMMAS;
+ Constant AND__TX      = ", and ";
+ #ifnot;
  Constant AND__TX      = " and ";
+ #endif;
  Constant WHOM__TX     = "whom ";
  Constant WHICH__TX    = "which ";
  
diff -c origlib/linklpa.h lib610/linklpa.h
*** origlib/linklpa.h	Sat Nov 13 11:29:36 1999
--- lib610/linklpa.h	Mon Sep 11 22:01:13 2000
***************
*** 71,76 ****
--- 71,79 ----
  Property with_key;
  Property door_dir;
  Property invent;
+ #ifdef USE_PARTINVENT; ! --Z
+ Property partinvent;
+ #endif; ! USE_PARTINVENT; ! --Z
  Property plural;
  Property add_to_scope;
  Property list_together;
diff -c origlib/parserm.h lib610/parserm.h
*** origlib/parserm.h	Sat Nov 13 11:29:22 1999
--- lib610/parserm.h	Sat Sep  2 11:02:42 2000
***************
*** 4447,4452 ****
--- 4447,4454 ----
  #iftrue LanguageContractionForms > 4;
     findout = true;
  #endif;
+ 
+ #ifndef SKIP_MAGIC_ARTICLES; ! --Z
     if (standard_interpreter ~= 0 && findout)
     {   StorageForShortName-->0 = 160;
         @output_stream 3 StorageForShortName;
***************
*** 4454,4459 ****
--- 4456,4462 ----
         @output_stream -3;
         acode = acode + 3*LanguageContraction(StorageForShortName + 2);
     }
+ #endif; ! SKIP_MAGIC_ARTICLES ! --Z
  
     print (string) artform-->acode;
     if (pluralise) return;
diff -c origlib/verblibm.h lib610/verblibm.h
*** origlib/verblibm.h	Sat Nov 13 11:29:14 1999
--- lib610/verblibm.h	Mon Sep 11 21:59:51 2000
***************
*** 425,430 ****
--- 425,446 ----
  [ WriteBeforeEntry o depth sentencepos  flag;
    if (c_style & INDENT_BIT ~= 0) Print__Spaces(2*(depth+wlf_indent));
  
+   #ifdef USE_PARTINVENT; ! --Z
+   if (c_style & PARTINV_BIT ~= 0)
+   {   if (o.partinvent~=0)
+       {   inventory_stage=1;
+           flag=PrintOrRun(o,partinvent,1);
+           if (flag==1)
+           {   if (c_style & ENGLISH_BIT ~= 0)
+               {   if (sentencepos == -1) print (string) AND__TX;
+                   if (sentencepos < -1) print ", ";
+               }
+               if (c_style & NEWLINE_BIT ~= 0) new_line;
+           }
+       }
+   }
+   #endif; ! USE_PARTINVENT; ! --Z
+ 
    if (c_style & FULLINV_BIT ~= 0)
    {   if (o.invent~=0)
        {   inventory_stage=1;
***************
*** 441,461 ****
    return flag;
  ];
  
! [ WriteAfterEntry o depth stack_p  flag flag2 flag3 p comb;
  
    if (c_style & PARTINV_BIT ~= 0)
!   {   comb=0;
!       if (o has light && location hasnt light) comb=comb+1;
!       if (o has container && o hasnt open)     comb=comb+2;
!       if ((o has container && (o has open || o has transparent))
!           && (child(o)==0)) comb=comb+4;
!       if (comb==1) L__M(##ListMiscellany, 1, o);
!       if (comb==2) L__M(##ListMiscellany, 2, o);
!       if (comb==3) L__M(##ListMiscellany, 3, o);
!       if (comb==4) L__M(##ListMiscellany, 4, o);
!       if (comb==5) L__M(##ListMiscellany, 5, o);
!       if (comb==6) L__M(##ListMiscellany, 6, o);
!       if (comb==7) L__M(##ListMiscellany, 7, o);
    }
  
    if (c_style & FULLINV_BIT ~= 0)
--- 457,484 ----
    return flag;
  ];
  
! [ WriteAfterEntry o depth stack_p  flag flag2 flag3 flag4 p comb;
  
    if (c_style & PARTINV_BIT ~= 0)
!   {   
!       #ifdef USE_PARTINVENT; ! --Z
!       inventory_stage=2;
!       flag4 = RunRoutines(o,partinvent);
!       #endif; ! USE_PARTINVENT; ! --Z
!       if (flag4 == 0) {
!           comb=0;
!           if (o has light && location hasnt light) comb=comb+1;
!           if (o has container && o hasnt open)     comb=comb+2;
!           if ((o has container && (o has open || o has transparent))
!               && (child(o)==0)) comb=comb+4;
!           if (comb==1) L__M(##ListMiscellany, 1, o);
!           if (comb==2) L__M(##ListMiscellany, 2, o);
!           if (comb==3) L__M(##ListMiscellany, 3, o);
!           if (comb==4) L__M(##ListMiscellany, 4, o);
!           if (comb==5) L__M(##ListMiscellany, 5, o);
!           if (comb==6) L__M(##ListMiscellany, 6, o);
!           if (comb==7) L__M(##ListMiscellany, 7, o);
!       }
    }
  
    if (c_style & FULLINV_BIT ~= 0)
***************
*** 505,510 ****
--- 528,540 ----
        objectloop (p in o)
            if (p hasnt concealed && p hasnt scenery) { flag3++; flag2 = p; }
    }
+ 
+   #ifdef USE_PARTINVENT; ! --Z
+   if (flag4) {  ! override the display of contents
+       flag3 = 0;
+       flag2 = 0;
+   }
+   #endif; ! USE_PARTINVENT; ! --Z
  
    if (c_style & ALWAYS_BIT ~= 0 && flag3>0)
    {   if (c_style & ENGLISH_BIT ~= 0) L__M(##ListMiscellany, 18, o);
_Shade_
Special source-code edition
Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
http://www.eblong.com/zarf/if.html

This is the source code for my 2000 IFComp entry.

This source code is provided for personal, educational use only. The
story and text of _Shade_ belong to me; you may not use them or create
derivative works which contain them. However, you have permission to
use the programming techniques of this game in your own works, and you
may use the source code excluding game text.

If you want to *play* _Shade_, you're in the wrong place. Go to my web
site, or the IF Archive, and download the compiled game file.
(http://www.eblong.com/zarf/ftp/shade.z5)

* Obvious Warning

This code contains spoilers! If you haven't played _Shade_, and you
read this code, you'll certainly ruin the game for yourself. Play it
first. I am releasing this code for the benefit of Inform programmers
who are familiar with _Shade_ and want to know how I did it.

* About The Source

This code is exactly the version I used to build _Shade_ release 3.
Okay, except that I converted tabs to spaces.

The lack of comments is, I'm afraid, the reader's cross to bear.

shade.inf: Main source file; includes all the others. Compile this one.
  Contains general-purpose routines, new grammar and verb routines,
  DrawStatusLine(), LibraryMessages(), ChooseObjects(), the player object, 
  etc.
shade-zone.inf: Support for the zone (sub-room) system. A base class
  for subrooms. A new ObjectIsUntouchable() routine.
shade-sand.inf: Sand objects and classes. (I had so much sand it was worth
  a separate source file.)
shade-goal.inf: The goal system -- tracks goals the player has achieved, 
  and runs appropriate code when a goal is attained. Also runs appropriate
  code when a goal becomes *possible*.
shade-room.inf: The top-level room and the minor sub-rooms (futon, desk).
  Contents thereof.
shade-nook.inf: The kitchen, bathroom, and shower. Contents thereof. Also
  water.
shade-fake.inf: The fake (reflected) apartment that appears late in the 
  game.
shade-dese.inf: The final (endgame) desert area.
library.patch: Diff of changes I made to library 6/10. See below.


* The Library

I compiled the game with a slightly modified version of the Inform
library (version 6/10).

I am not including the modified 6/10 library in this package (for
reasons both of space and of copyright), but the file "library.patch"
contains the differences.

There are, in fact, three differences:

  - If AMERICAN_COMMAS is defined (which _Shade_ does), the "and" in
object lists has a comma before it. This prevents strange list phrases
such as "my parents, Ayn Rand and God".

  - If SKIP_MAGIC_ARTICLES is defined (which _Shade_ does), the parser
does not attempt to magically infer the correct article ("a" or "an")
before objects. I don't like to rely on this feature, because it's not
supported on all interpreters, and it slows down execution in any
case. _Shade_ always uses the article property when appropriate, so
the magic feature is not necessary.

  - If USE_PARTINVENT is defined (which _Shade_ does), the list writer
supports a property "partinvent". This works more or less like
"invent", except that it works for lists when PARTINV_BIT is set
(instead of FULLINV_BIT). This lets me adjust objects in room listings
just the way "invent" lets me adjust them in inventory listings.

! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


[ ParseNameRelay wd num;
    wd = NextWord();
    while (WordInProperty(wd, self.self_obj, name)) {
        num++;
        wd = NextWord();
    }
    return num;
];

[ DesertCheckUnder obj;
    if (figure.number == obj) {
        "You catch a glimpse of two tiny eyes, shining out from beneath ",
            (the) obj, ".";
    }
    "You bend down, and see only sand.";
];

[ DesertGoaled obj;
    remove obj;
    if (figure.number == obj) {
        figure.number = ChooseHome();
        if (figure.number) {
            "^A tiny figure burrows out of the new-fallen sand. You
                just have time to make out its shape -- human --
                before it darts behind you and out of sight.";
        }
        else {
            "^A tiny figure burrows out of the new-fallen sand.
                It darts one way, then the other; but there is no
                more shelter.";
        }
    }
];

[ ChooseHome ix obj count;
    count = 0;
    ix = random(101);
    while (count < 3) {
        switch (ix % 3) {
            0: obj = desstereo;
            1: obj = descomputer;
            2: obj = desfuton;
        }
        if (obj in Desert)
            return obj;
        ix++;
        count++;
    }
    return 0;
];

[ Suffer;
    if (figure.number)
        "^[BUG] figure.number nonzero";
    figure.weakness++;
    switch (figure.weakness) {
        2: print "^The figure moves more slowly now.^";
        3: print "^The figure slumps to the sand. No -- it's still moving,
            though on hands and knees.^";
        4:
            print "^The figure falls. After a few seconds... it still
                doesn't move.^^The sands quickly cover it.^";
            remove figure;
            player.weakness = 0;
            player.orders = EndSuffering;
    }
];

[ EndSuffering;
    if (action == ##Take && noun == book) {
        <<Examine book>>;
    }
    if (action ~= ##Wait or ##Look or ##Sleep or ##Wake or ##Inv 
        or ##Examine or ##Search)
        "Nothing to do but wait.";
    player.weakness++;
    if (player.weakness < 3 || book.saw_blank == false) {
        return false;
    }
    print "The tiny figure crawls out from under the sands. It's 
        dead.^^";
    print "~You win,~ it says. ~Okay, my turn again.~^^";
    print ">...^Nothing left to do. Time passes.^^";
    print "The sun crawls higher.^";
    deadflag = 2;
];

Object Desert "home"
  with
    articles "Your" "your" "your",
    seenonce false,
    description [;
        print "The land is nearly barren. Dunes roll away towards the
            rising sun; a little shade trails behind them, but that will
            soon be gone. The sky is flat and empty blue.^";
        DescribeParagraph(self);
        rtrue;
    ],
    startup [;
        StartDaemon(figure);
        StartDaemon(desstereo);
    ],
    before [;
        Go: 
            "You pace around for a while.";
        Exit:
            "But the taxi isn't here yet!";
        Listen:
            if (noun == 0) {
                if (desstereo in self)
                    <<Listen desstereo>>;
                "You hear nothing. The helicopter is long gone.";
            }
        Examine:
            switch (noun) {
                d_obj:
                    <<Examine desertsand>>;
                u_obj:
                    <<Examine desertsky>>;
            }
            rfalse;
    ];

Object -> desertsand "sand"
  with
    name 'sand' 'sands' 'dune' 'dunes',
    article "the",
    description "It's exactly like all the sand you've ever seen before.",
    before [;
        Search:
            <<Examine self>>;
        BlowOn:
            "A few grains of sand tumble away.";
        Take:
            "You pick up a pinch of sand. It runs through your fingers.";
        Push, Pull:
            "You push the sand around with your foot.";
        Dig:
            "You dig for a while.";
    ],
  has scenery;

Object -> desertshade "shade"
  with
    name 'shade' 'shadow',
    description "The shadows shrink as you watch.
        The dunes aren't high here, and the sun is rising fast.",
    before [;
        Search: <<Examine self>>;
        Examine: rfalse;
        Enter: "Even lying flat, you can no longer shield yourself behind
            the dunes.";
        default: "They're only shadows.";
    ],
  has scenery;

Object -> desertsky "sky"
  with
    name 'sky',
    description "The sky is dead blue, crushed by sunlight.",
    before [;
        Search: <<Examine self>>;
        Examine: rfalse;
        default: "There's nothing you can do to the sky.";
    ],
  has scenery;

Object -> desertsun "sun"
  with
    name 'sun' 'sunlight' 'light',
    description "Fusing hydrogen. Deathly, inescapable. White, blazing down.",
    before [;
        Search: <<Examine self>>;
        Examine: rfalse;
        default: "There's nothing you can do to the sun.";
    ],
  has scenery;

Object -> figure "tiny figure"
  with
    name 'tiny' 'figure' 'shape' 'human' 'eyes' 'movement',
    description [;
        if (self.number)
            "You still haven't managed to see it clearly.";
        print "A tiny human figure";
        switch (self.weakness) {
            0: print " walks";
            1: print " trudges";
            2: print " staggers";
            default: print " crawls";
        }
        " across the sand. It doesn't seem to
            notice you.";
    ],
    nosubdescribe [; return (self.number ~= 0); ],
    subdescribe [;
        print "A tiny figure inches across the sand.";
        rtrue;
    ],
    number desfuton,
    weakness 0,
    before [;
        Find:
            if (self.number)
                "You're not sure where it went.";
            "It's right here.";
        Search:
            if (self.number)
                "You're not sure where it is.";
            "You try to peer into the figure's eyes. They are too remote.
                They do not see you.";
        Push, Pull, Turn, Lower:
            if (self.number)
                "You're not sure where it is.";
            print "You push a wave of sand at the figure. It stumbles and
                slides under -- quickly buried. After a few seconds, grains
                of sand roll aside, and it pulls itself to the surface.^";
            Suffer();
            rtrue;
        Touch:
            if (self.number)
                "You're not sure where it is.";
            print "You kneel and brush your finger over the figure's
                forehead. It shakes its head, unseeing, and then collapses.
                You watch in concern, but in a few moments it struggles to
                its feet again.^";
            Suffer();
            rtrue;
        Attack:
            if (self.number)
                "You're not sure where it is.";
            print "You lean down and slap at the tiny figure. The blow
                does not connect, but it whips up sand; the figure
                ducks its head and shields its eyes from the showering
                grit.^";
            Suffer();
            rtrue;
        Take, Raise:
            if (self.number)
                "You're not sure where it is.";
            print "You cup your palms around the figure and raise it into
                the air -- a human shape fallen to its knees in your handful
                of sand. You gaze down;
                sunlight seethes in the cauldron of your fingers.
                The figure slowly slides flat. But the sand is running
                out of your hands, 
                and the figure falls to the ground, where it slowly
                pulls itself up again.^";
            Suffer();
            rtrue;
        Wave:
            if (self.number)
                "You're not sure where it is.";
            print "You stomp your foot. The sand ripples silently, and
                the figure trips and falls. Quickly it stands again,
                and continues.^";
            Suffer();
            rtrue;
        BlowOn:
            if (self.number)
                "You're not sure where it is.";
            print "You crouch and blow at the tiny figure. Hot sand flurries
                up. After several seconds, the figure stumbles out of the
                cloud of sand, covering its eyes.^";
            Suffer();
            rtrue;
        Listen:
            "You can make out no words.";
    ],
    life [;
        Kiss:
            if (self.number)
                "You're not sure where it is.";
            if (verb_word == 'kiss') {
                print "You kneel and brush your lips over the figure's
                    forehead. It shakes its head, unseeing, and then collapses.
                    You watch in concern, but in a few moments it struggles to
                    its feet again.^";
            }
            else {
                print "You wrap your hand gently around the figure for
                    a moment. When you release it, the figure falls
                    prostrate, flushed and dry-looking. Finally it
                    twitches and regains its feet.^";
            }
            Suffer();
            rtrue;
        Give, Show:
            if (self.number)
                "You're not sure where it is.";
            "The figure doesn't seem to see you.";
        Ask, Tell, Answer, Order:
            if (self.number)
                "You're not sure where it is.";
            print "As you speak, currents of air curl across the sand.
                The figure clutches its clothing about itself and
                staggers on, ignoring you.^";
            Suffer();
            rtrue;
    ],
    opentime 0,
    daemon [ ix;
        self.opentime++;
        if (parent(self) == 0) {
            StopDaemon(self);
            return;
        }
        
        if (self.number == 0) {
            if (self.opentime >= random(3)+1) {
                self.opentime = 0;
                switch (random(97) % 7) {
                    0: print "^The figure looks around, shading its eyes,
                        and then sets out in a slightly different direction.^";
                    1: print "^The figure seems to be muttering to itself.^";
                    2: print "^The figure seems to be chanting to itself.^";
                    3: print "^The figure stops, digs for a moment in the
                        sand, and then continues.^";
                    4: print "^The figure stops and looks around for a moment,
                        shading its eyes.^";
                    5: print "^The figure searches its clothing briefly, and
                        then continues on its way.^";
                    6: print "^The figure closes its eyes for a few seconds.^";
                }
            }
            return;
        }
        
        if (self.opentime == 2 && random(99) < 60) {
            print "^Something moves beneath ", (the) self.number, ".^";
            return;
        }
        if (self.opentime >= random(3)+2) {
            ix = ChooseHome();
            if (ix ~= self.number) {
                self.opentime = 0;
                self.number = ix;
                print "^You catch a darting movement past your feet.^";
            }
        }
    ],
  has scenery animate neuter;

Object -> desfuton "futon"
  with
    self_obj futon,
    parse_name ParseNameRelay,
    articles "Your" "your" "your",
    subdescribe "Your futon sits nearby.",
    description [ ix;
        print "The futon is definitely on the downhill side of the dunes.
            The frame is half-buried and a few lines of sand trickle down
            the sheet-wrinkles, but it's the same old futon in the end.^";
        ix = children(self);
        if ((player notin self && ix > 0) || ix > 1) {
            print "^Sitting on the futon";
            if (player in self)
                print " next to you";
            WriteListFrom(child(self),
                TERSE_BIT + ENGLISH_BIT + ISARE_BIT + CONCEAL_BIT);
            print ".^";
        }
        rtrue;
    ],
    before [;
        Enter:
            print "You sit down, and find yourself seated on bare sand.
                The futon has disintegrated without a sound or a final
                word.^";
            while (child(self)) {
                move child(self) to Desert;
            }
            DesertGoaled(self);
            rtrue;
        LookUnder:
            DesertCheckUnder(self);
            rtrue;
        Pull, Push, Turn:
            "You can barely move the futon on a solid floor. Against
                the sand, no hope.";
        Dig:
            "Still no loose change.";
    ],
    react_before [;
        Exit:
            if (player in self) {
                move player to parent(self);
                if (keep_silent == 0) {
                    print "You stand up.^";
                }
                rtrue;
            }
    ],
  has supporter enterable transparent scenery;

Object -> desstereo "stereo"
  with
    self_obj stereo,
    parse_name ParseNameRelay,
    subdescribe "The stereo leans against a dune.",
    description [;
        print "The stereo is old, dating from the era when matte-black,
            featureless, and opaque were the watchwords of hipness. Now it 
            sits on a pile of sand and looks lost.... ";
        self.song();
        " is playing.";
    ],
    before [;
        LookUnder:
            DesertCheckUnder(self);
            rtrue;
        SwitchOn:
            "The stereo is already on.";
        SwitchOff:
            print "A garbled news report comes on just as you reach for
                the switch. ~Death Val... hikers... missing for... still... 
                hope, say...~^^...Old news. You hit the switch: click.
                The stereo falls silent, turns white, and drops
                instantly away into sand.^";
            DesertGoaled(self);
            rtrue;
        Listen:
            print "The stereo is playing ";
            self.song();
            ".";
    ],
    opentime 99,
    number 0,
    song [;
        switch (self.number) {
            1: print "~Cool, Clear Water~";
            2: print "~Gotta Stop on the Road for Water~";
            3: print "~Water From Another Time~";
            4: print "~Sun Gets In My Eyes~";
            5: print "~You Listenin' To Me?~";
            6: print "~Ain't Gonna Make It~";
            7: print "~Projection Of Your Fears~";
            8: print "~Take It Out On The Little Guy~";
            9: print "~Rule the Kingdom of the Sun~";
            10: print "~Triumph Alone~";
            11: print "~Alone~";
            12: print "~Shade~";
        }
    ],
    daemon [;
        if (parent(self) == 0) {
            StopDaemon(self);
            return;
        }
        self.opentime++;
        if (self.opentime >= random(3)+2) {
            self.number++;
            self.opentime = 0;
            if (self.number > 12)
                self.number = 12;
            print "^Click. The stereo is playing ";
            self.song();
            print ".^";
        }
    ],
  has scenery;

Object -> descomputer "computer"
  with
    self_obj computer,
    parse_name ParseNameRelay,
    subdescribe "Your computer squats complacently, the color of the sand.",
    description [;
        if (self.number == 0)
            "The game's opening sits neatly at the top of the screen,
                followed by a command prompt.";
        else
            "The game's closing message blinks at the bottom of the screen.";
    ],
    number 0,
    before [;
        Search:
            <<Examine self>>;
        Play, TypeOn:
            print "You";
            if (random(97) < 50)
                print " kneel";
            else
                print " crouch";
            if (random(103) < 66)
                print " in the sand";
            if (self.number == 0)
                print " and play through the computer game again.";
            else
                print ", restart, and play through once more.";
            self.number++;
            switch (self.number) {
                2: print " You don't get any farther.";
                3: print " A different ending, this time, but it comes
                    to the same thing.";
                5: print " You aren't going to get anywhere.";
                7: print " Nothing ever changes.";
                9: print " You resolve to take charge.";
                12: print " You plan to make someone else pay for your
                    mistakes.";
                15: print " You intend to switch places.";
                19: print " If you win, you win alone.";
                24: print " You wish for shade.";
                28: self.number = 23;
            }
            new_line;
            rtrue;
        LookUnder:
            DesertCheckUnder(self);
            rtrue;
        SwitchOn:
            "The computer is already on.";
        SwitchOff:
            print "~The only way to win is not to play,~ flashes the
                screen. You roll your eyes. The oldest lie; nobody's
                bought it since ever.^^";
            print "You hit the power key; the computer gives a tiny sigh and
                crumbles away.^";
            DesertGoaled(self);
            rtrue;
        Open:
            "This model can't be opened except at the factory.";
        Smell:
            "The computer smells of hot silica.";
    ],
  has scenery;


! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


Global fake_counter = 0;

Class Fake
  with
    self_obj 0,
    short_name [;
        print (name) self.self_obj;
        rtrue;
    ],
    parse_name [ obj wd num;
        if (self provides name) {
            wd = NextWord();
            while (WordInProperty(wd, self, name)) {
                num++;
                wd = NextWord();
            }
            return num;
        }
        
        obj = self.self_obj;
        if (obj provides parse_name)
            return obj.parse_name();
            
        wd = NextWord();
        while (WordInProperty(wd, obj, name)) {
            num++;
            wd = NextWord();
        }
        return num;
    ],
    before [;
        Examine, Search:
            "You can't quite make out anything unusual.";
        Find:
            "It's here.";
        default:
            ! print_ret "[DEBUG] ", (the) self, ".";
            fake_counter++;
            if (action == ##Enter && (noun == fakefuton or fakedesk) 
                && fake_counter == 3) {
                fake_counter = 4;
            }
            switch (fake_counter) {
                1: "You take a step across the room, but a quick movement
                    distracts you. Something scurried along the wall?
                    You can't see it any more.";
                2: "You move towards ", (the) self, " but feel suddenly
                    dizzy. Dehydration, probably.";
                3: "You need to sit down.";
                4: 
                    print "You reach out and touch your hand to ", (the) self,
                        ". ";
                    if (self.self_obj has pluralname)
                        print "They";
                    else
                        print "It";
                    print " feels like glass.^^";
                    print "The reflection shimmers like water, but the mirror
                        is only heat -- pooling among the dunes, 
                        rising from the sands.^^";
                    KeyPause();
                    print "^^^";
                    move book to Desert;
                    Desert.startup();
                    PlayerTo(Desert);
                    rtrue;
            }
    ],
  has scenery;

Object FakeApartment "apartment"
  with
    articles "Your" "your" "your",
    seenonce false,
    description [;
        if (~~self.seenonce) {
            self.seenonce = true;
            print "Odd, how the sunlight just makes your apartment
                more prosaic.^^";
            print "Not much of an apartment, no.";
        }
        else {
            print "You survey your one small room.";
        }
        print " One desk, paper-piled, with a dusty computer shoved
            to the side. Your futon. Second-hand stereo sitting on a
            cardboard crate. A kitchen nook one way and a bathroom nook
            the other, with a closet to the side. A broad mirror tries
            to make the place seem twice its size; it halfway works. One
            window, whose shade is up, and the front door wide open.^";
        print "^Your luggage is piled untidily by the door.
            A potted hyacinth sits beneath the window.^";
        rtrue;
    ],
    before [;
        Vacuum: <<Take fakevacuum>>;
        Go: 
            "You paced enough last night.";
        Exit:
            "The taxi hasn't arrived, although it's about to.";
    ];

Fake -> fakelivingroom
  with
    self_obj apartmentproxy,
    before [;
        Examine, Search: <<Look>>;
        default: "What?";
    ];

Fake -> fakefrontdoor
  with
    self_obj frontdoor,
    before [;
        Enter: <<Exit>>;
    ];

Fake -> fakewindow
  with
    self_obj window;

Fake -> fakewindowshade
  with
    self_obj windowshade;

Fake -> fakedesk "desk"
  with
    self_obj desk,
  has supporter;

Fake -> -> faketodo
  with
    self_obj todo,
    articles "Your" "your" "your",
  has ~scenery;
    
Fake -> -> fakebook
  with
    self_obj book,
  has ~scenery;

Fake -> fakeplant
  with
    name 'pot' 'potted' 'plant' 'soil' 'hyacinth',
    short_name "hyacinth",
    self_obj plant;

Fake -> fakeluggage
  with
    self_obj luggage;

Fake -> fakekitchen
  with
    self_obj kitchen;

Fake -> fakecounter
  with
    self_obj counter;

Fake -> fakecupboard
  with
    self_obj cupboard;

Fake -> fakestove
  with
    self_obj stove;

Fake -> fakekitchensink
  with
    self_obj kitchensink;

Fake -> fakefridge
  with
    self_obj fridge;

Fake -> fakeglass
  with
    self_obj glass;

Fake -> fakebathroom
  with
    self_obj bathroom;

Fake -> faketoilet
  with
    self_obj toilet;

Fake -> fakebathsink
  with
    self_obj bathsink;

Fake -> fakeshower
  with
    self_obj shower;

Fake -> fakeshowerhead
  with
    self_obj showerhead;

Fake -> fakefuton
  with
    self_obj futon;

Fake -> fakecloset
  with
    self_obj closet;

Fake -> fakevacuum
  with
    self_obj vacuum;

Fake -> fakejacket
  with
    self_obj jacket;

Fake -> fakepapers
  with
    self_obj papers;

Fake -> fakelamp
  with
    self_obj lamp;

Fake -> fakecomputer
  with
    self_obj computer;

Fake -> faketickets
  with
    self_obj tickets;

Fake -> fakestereo
  with
    self_obj stereo;

Fake -> fakecrate
  with
    self_obj crate;

Fake -> fakemirror
  with
    self_obj mirror;

! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


Constant MAXIMPS 12;
Array goalimps --> MAXIMPS;
Global impcount = 0;
Global goalcount = 0;

Object goalhandler
  with
    initial [;
        StartDaemon(self);
    ],
    imp [; "[BUG] no imp"; ],
    daemon [ ix jx kx;

        #ifdef DEBUG;
        if (debug_flag & 4 ~= 0) {
            for (ix=0 : ix<impcount : ix++) {
                print "...", (name) goalimps-->ix, ": imp^";
            }
        }
        ! not entirely accurate; if an imp calls Goaled(Obj), the object's
        ! imp will be run that turn also.
        #endif;

        for (ix=0 : ix<impcount : ix++) {
            jx = goalimps-->ix;
            if (jx.imp == NULL)
                kx = -1;
            else
                kx = jx.imp();
            if (kx == -1) {
                for (kx=ix : kx<impcount-1 : kx++) {
                    goalimps-->kx = goalimps-->(kx+1);
                }
                impcount--;
                ix--;
            }
        }
    ];

[ Goaled obj;
    if (~~(obj provides prereqs)) {
        print "[BUG] Goaled(", (name) obj, "): no prereqs^";
        rfalse;
    }
    if (obj has goal_complete)
        return;
    goalcount++;
    give obj goal_complete;
    if (obj provides imp) {
        goalimps-->impcount = obj;
        impcount++;
    }
];

[ GoalOpen obj   addr num ix jx;
    if (~~(obj provides prereqs)) {
        print "[BUG] GoalOpen(", (name) obj, "): no prereqs^";
        rfalse;
    }
    if (obj has goal_complete)
        rfalse;
    addr = obj.&prereqs;
    num = (obj.#prereqs) / WORDSIZE;
    for (ix=0 : ix<num : ix++) {
        jx = addr-->ix;
        if (jx == true)
            continue;
        switch (metaclass(jx)) {
            Object:
                if (jx hasnt goal_complete)
                    rfalse;
            Routine:
                if (jx() == false)
                    rfalse;
            default:
                print_ret "[BUG] GoalOpen(", (name) obj, "): strange value ",
                    jx;
        }
    }
    rtrue;
];

[ GoalClosed obj;
    return (obj has goal_complete);
];

[ GoalEverOpen obj;
    if (obj has goal_complete)
        rtrue;
    return GoalOpen(obj);
];

[ KillImp obj;
    if (~~(obj provides imp)) {
        print_ret "[BUG] KillImp(", (name) obj, "): no imp";
    }
    obj.imp = NULL;
];

#ifdef ZDEBUG;

[ GoalsScope  obj;
    if (scope_stage == 1)
        rfalse;
    if (scope_stage == 2) {
        objectloop (obj provides prereqs) {
            PlaceInScope(obj);
        }
        rtrue;
    }
    "That's no goal.";
];

[ GoalListSub obj isopen isclosed any;
    ! 0: open and closed. 1: all. 2: only open. 3: only closed.
    objectloop (obj provides prereqs) {
        isopen = GoalOpen(obj);
        isclosed = GoalClosed(obj);
        any = false;
        if (isopen && noun == 0 or 1 or 2) {
            any = true;
            print (name) obj, ": open^";
        }
        if (isclosed && noun == 0 or 1 or 3) {
            any = true;
            print (name) obj, ": closed^";
        }
        if (noun == 1 && (~~any)) {
            print (name) obj, ": not yet^";
        }
    }
];

[ ZapOpenGoalSub  addr num ix;
    print "Opening: ", (the) noun, "^";
    addr = noun.&prereqs;
    num = (noun.#prereqs) / WORDSIZE;
    for (ix=0 : ix<num : ix++) {
        addr-->ix = true;
    }
];

[ ZapCloseGoalSub;
    print "Closing: ", (the) noun, "^";
    Goaled(noun);
];

#endif;

! --- individual goal utilities

Object firstgoal
  with
    name 'firstgoal',
    prereqs true,
    number 0,
    imp [; 
        self.number++;
        switch (self.number) {
            2: print "^Come to think of it, as with a hangover, dehydration
                is probably the problem. Your mouth is dry wool.^";
            7: print "^You're still feeling thirsty.^";
            15: print "^You run your tongue over dry teeth.^";
            16: self.number = 3;
        } 
    ];

Global background_counter = 0;
[ BackgroundInfo obj str;
    if (obj.background_flag)
        rtrue;
    obj.background_flag = true;
    background_counter++;
    
    print "^(";
    if (str) 
        print (string) str, " ";
        
    switch (background_counter) {
        1: print "It is, of course, the Death Valley Om -- half arts
festival, half cult, a week in the deep desert where people show off,
have sex, take drugs, and maintain a twenty-four-hour constant ", 
(emph) "OM.", 
" Sand, heat, and thousands of throats. A space outside the world; with 
no dues except that you join in the chant, as much as is in you to 
chant.";

        2: print "Nothing is off-limits at the Om. ~Law ends at sea level.~
In one place in the country, for one week, a community is formed from
pure will. No enforcement; only desire, and the knowledge of what the
desert can do. And the Om. Every religion chants, you remember reading
that.";

        3: print "The experience is supposed to be transformative. And,
frankly, you're in need of transformation. Another month of this and
you'll indistinguishable from this apartment -- beige, featureless, and
up for cheap rent. At the Death Valley Om, they say, you may be sunburnt
and thirsty and exhausted, but you're alive. Nothing sounds better.
You're planning to try the solo Zen night hiking.";

        default: print "[BUG] background info";
    }
    
    ")";
];

Object tickets "plane tickets"
  with
    name 'plane' 'airplane' 'ticket' 'tickets',
    description [;
        print "Your round-trip tickets to California.";
        if (~~self.seenonce) {
            self.seenonce = true;
            print " You stare at them for a moment -- lost in the idea
                of the desert.";
        }
        new_line;
        rtrue;
    ],
    seenonce false,
    after [;
        Take:
            if (self hasnt general) {
                give self general;
                "Taken. Something scrapes underfoot as you bend to pick
                    the tickets up.";
            }
    ],
    prereqs water,
    imp [;
        StartMotorNoise();
        BumpSand(livingsand);
        return -1;
    ],
  has rescuable pluralname; ! general means taken

Global ticket_counter = 0;
[ CheckTicket obj;
    if (obj.ticket_search >= 2) {
        if (obj.ticket_search == 3)
            "No matter how often you look, the plane tickets aren't there.";
        obj.ticket_search = 3;
        "The plane tickets still aren't there.";
    }
    obj.ticket_search = 2;
    ticket_counter++;
    if (ticket_counter < 3) {
        "Nope. The tickets aren't there.";
    }
    Goaled(tickets);
    move tickets to Apartment;
    print_ret "Nope. The tickets aren't -- ", (emph) "Aha.", " They are,
        after all. The tickets slide to the floor and lie there, smirking
        at you.";
];

[ StartMotorNoise;
    move motornoise to Apartment;
    StartDaemon(motornoise);
];

[ StopMotorNoise;
    StopDaemon(motornoise);
    remove motornoise;
];

Object motornoise "sounds"
  with
    name 'sound' 'sounds' 'helicopter' 'copter',
    before [;
        Listen:
            "You can hear a helicopter somewhere up in the night.";
        Examine:
            <<Listen self>>;
        Find:
            "Somewhere outside and above.";
        default:
            "You can only hear the helicopter.";
    ],
    number 0,
    daemon [;
        self.number++;
        switch (self.number) {
            3: print "^The sound of a helicopter comes faintly through the
                closed window.^";
            5: print "^The helicopter is getting closer.^";
            7: print "^The sound of the helicopter is fading now.^";
            10: print "^You can barely hear the helicopter any more.^";
            11: StopMotorNoise();
        }
    ],
  has scenery pluralname;

! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


Class Sink
  with
    name 'sink' 'tap' 'faucet',
    short_name [;
        print (string) self.self_name, " sink";
        rtrue;
    ],
    parse_name [ wd firstwd num;
        wd = NextWord();
        firstwd = wd;
        while (WordInProperty(wd, self, name)) {
            num++;
            wd = NextWord();
        }
        if (num == 1 && firstwd == self.self_word)
            return 0;
        return num;
    ],
    before [ ix;
        Enter:
            if (verb_word == 'run')
                <<SwitchOn self>>;
        Examine:
            if (OIU(self)) rtrue;
        Receive:
            "That sort of juggling is unlikely to help.";
        EmptyT:
            ix = self.self_obj;
            <<Insert ix second>>;
        SwitchOff:
            if (OIU(self)) rtrue;
            "The ", (string) self.self_name, " sink isn't running.";
    ],
    receive_water [;
        if (GoalClosed(self))
            "You pour the water into the sink, where it soaks into the
                sand.";
        "You pour the water down the drain.";
    ],
    receive_sand [;
        if (GoalClosed(self))
            "You add your sand to the sink's burden.";
    ],
  has scenery transparent;

Class GeneralWater
  with
    name 'water' 'from' 'tap' 'faucet',
    is_sink false,
    short_name [;
        print "water from the ", (string) self.self_name;
        if (self.is_sink)
            print " sink";
        rtrue;
    ],
    self_name "BUG",
    parse_name [ wd gotwater num;
        wd = NextWord();
        while (WordInProperty(wd, self, name)) {
            if (wd == 'water')
                gotwater = true;
            num++;
            wd = NextWord();
        }
        if (~~gotwater)
            return 0;
        return num;
    ],
    before [;
        Enter:
            if (verb_word == 'run')
                <<SwitchOn self>>;
        Find:
            "Try the ", (string) self.self_name, ".";
        Take:
            if (OIU(self)) rtrue;
            if (glass notin player)
                "You have nothing in which to carry water.";
            <<Insert self glass>>;
        Drink, Taste:
            "You broke that habit of drinking from the faucet when you
                were eleven.";
        default:
            print "The ", (string) self.self_name;
            if (self.is_sink)
                print " sink";
            " isn't running right now.";
    ],
  has scenery;

Class SinkWater
  class GeneralWater,
  with
    is_sink true,
    name 'sink';

Zone kitchen "kitchen nook"
  with
    name 'kitchen' 'nook' 'alcove',
    description [ num;
        print "The kitchen alcove
            has a refrigerator, a sink, a stove, and barely enough
            space to stand between them.";
        if (cupboard in self)
            print " One wall projects out to
                form a counter, with a cupboard beneath it.";
        new_line;
        DescribeParagraph(self);
        num = Locale(self, "You see", "You also see");
        if (num) {
            print " lying";
            if (player in self)
                print " here";
            else
                print " on the kitchen floor";
            print ".^";
        }
        rtrue;
    ],
    nolocifin true,
    before [;
        Examine:
            if (OIU(self)) rtrue;
            rfalse;
        Search:
            <<Examine self>>;
    ],
    prereqs cupboard;

Sink -> kitchensink
  with
    name 'kitchen',
    self_word 'kitchen',
    self_name "kitchen",
    self_obj kitchensinkwater,
    description [;
        if (GoalClosed(self))
            "The kitchen sink is full of sand.";
        "The kitchen sink is spotless. Not through any virtue of
            yours, mind you, except for your inability to cook.";
    ],
    before [;
        SwitchOn, Turn:
            if (OIU(self)) rtrue;
            if (GoalClosed(self))
                "More sand pours from the kitchen sink's faucet.";
            if (GoalOpen(self)) {
                Goaled(self);
                remove self.self_obj;
                self.self_obj = kitchensinksand;
                move self.self_obj to self;
                BumpSand(kitchen);
                "With an eerie hiss, dry white sand boils from the faucet.
                    You yank at the tap, but the kitchen sink is already
                    full, and sand spilling on the floor.";
            }
            "Nothing comes from the tap but an eerie distant howling.
                The kitchen sink hasn't worked since you moved in.";
    ],
    imp [;
        closet.startup();
        return -1;
    ],
    prereqs pbutter crackers;

SinkWater -> -> kitchensinkwater
  with
    name 'kitchen',
    self_word 'kitchen',
    self_name "kitchen",
    before [;
        Insert:
            if (OIU(self)) rtrue;
            if (second ~= glass)
                "That won't hold water.";
            if (glass notin player)
                "You're not holding the glass.";
            if (water in glass)
                "The glass is already full.";
            <<SwitchOn kitchensink>>;
        SwitchOn:
            <<SwitchOn kitchensink>>;
    ];

Object -> counter "counter"
  with
    name 'counter',
    short_name [;
        if (FindZone(player) ~= kitchen)
            print "kitchen ";
        print "counter";
        rtrue;
    ],
    description [;
        self.subdescribe();
        new_line;
        rtrue;
    ],
    nosupportlist true,
    nosubdescribe [; return (child(self) == 0); ],
    subdescribe [ ix;
        ix = child(self);
        if (~~ix) {
            print (The) self, " is bare.";
        }
        else {
            print "On ", (the) self;
            WriteListFrom(ix,
                TERSE_BIT + ENGLISH_BIT + ISARE_BIT + CONCEAL_BIT);
            print ".";
        }
        rtrue;
    ],
    before [;
        Search: <<Examine self>>;
        Attack, Touch, Push, Pull, Receive, LetGo:
            if (GoalOpen(cupboard))
                <<Touch cupboard>>;
    ],
  has scenery supporter;

Object -> stove "stove"
  with
    name 'stove' 'stovetop' 'electric' 'range' 'burner',
    seenonce false,
    description [;
        if (GoalClosed(self)) 
            "Nothing is left of the stovetop but a corroded metal sheet,
                dusted with sand.";
        print "The usual electric range arrangement.";
        if (~~self.seenonce) {
            self.seenonce = true;
            print " Weirdly, there is
                no oven in your kitchen. You don't know what lurks in the
                space beneath the stove. You've been afraid to pry -- the
                stove might turn out to be powered by radioactive decay,
                or something.";
        }
        if (self has on) {
            print " The stove is on";
            if (self.heat >= 4)
                print " and the burner is glowing red";
            print ".";
        }
        else {
            if (self.heat >= 4)
                print " The burner is still red, though dimming.";
        }
        new_line;
        rtrue;
    ],
    before [;
        Receive: 
            if (OIU(self)) rtrue;
            "What, after all these years you're going to take up
                cooking?";
        SwitchOn:
            if (OIU(self)) rtrue;
            if (GoalClosed(self)) 
                "Click. Nothing happens. Nor will it ever again, not with
                    this stove.";
            if (GoalOpen(self)) {
                Goaled(self);
                give self ~on;
                self.heat = 0;
                StopDaemon(self);
                BumpSand(kitchensand);
                "You turn the knob -- click. Then the stove emits a loud
                    crackle. The burner frosts white, although you can feel
                    the heat radiating from it; bits of its substance seem
                    to be flaking away. Thermal shock? you have time to
                    think. And then the entire burner arrangement
                    slumps into white sand, which runs down off the stove.";
            }
            if (self has on) "The stove is already on.";
            give self on;
            StartDaemon(self);
            "Click.";
        SwitchOff:
            if (OIU(self)) rtrue;
            if (GoalClosed(self)) 
                "The stove is about as off as you could possibly
                    hope for.";
            if (self hasnt on) "The stove is already off.";
            give self ~on;
            "Click.";
        Touch:
            if (OIU(self)) rtrue;
            switch (self.heat) {
                0: rfalse;
                1: "The burner is warm.";
                2: "The burner is very warm.";
                default: "The burner is too hot to touch.";
            }
    ],
    heat 0,
    daemon [;
        if (self has on && GoalOpen(self)) {
            give self ~on;
            print "^The stove emits a peculiar crackle.^";
        }
        if (self has on) {
            self.heat++;
            if (self.heat > 5)
                self.heat = 5;
            if (FindZone(player) == kitchen) {
                switch (self.heat) {
                    2: print "^Warmth begins to radiate from the stove.^";
                    4: print "^The stove burner is glowing red now.^";
                }    
            }
        }
        else {
            self.heat--;
            if (self.heat < 0) {
                self.heat = 0;
                StopDaemon(self);
            }
            if (FindZone(player) == kitchen) {
                switch (self.heat) {
                    3: print "^The burner's glow fades.^";
                }    
            }
        }
    ],
    prereqs [; return (GoalClosed(bathsink) || GoalClosed(kitchensink)); ],
  has scenery ~on;
  
Object -> cupboard "cupboard"
  with
    name 'cupboard' 'cabinet',
    description [;
        print "Your only kitchen cupboard is a small space beneath the
            counter, shelved in warped pressboard.";
        if (self has open) {
            print " ";
            self.subdescribe();
            if (GoalOpen(self))
                print " At the very back, sand falls from an upper corner,
                    a few grains at a time.";
        }
        else {
            print " The cupboard is closed.";
        }
        new_line;
        rtrue;
    ],
    nosubdescribe [; return (self hasnt open); ],
    subdescribe [ ix;
        print "The cupboard is open";
        ix = child(self);
        if (~~ix) {
            print " and bare";
        }
        else {
            print "; inside";
            WriteListFrom(ix,
                TERSE_BIT + ENGLISH_BIT + ISARE_BIT + CONCEAL_BIT);
        }
        print ".";
        rtrue;
    ],
    before [;
        Enter:
            if (self has open)
                "You don't fit.";
            else
                return L__M(##Insert,3,self);
        Touch:
            if (GoalOpen(self)) {
                RescueContents(self, kitchen);
                RescueContents(counter, kitchen);
                remove self;
                remove counter;
                BumpSand(kitchen);
                plant.startup(2);
                Goaled(self);
                print "The cabinet and counter start to groan as soon as you
                    touch them. You";
                if (self hasnt open)
                    print " yank the cabinet open, and slam it";
                else
                    print " slam the cabinet";
                " for good measure; and the stained pressboard crackles
                    white, shivers, and explodes into sand. All right!";
            }
        Open:
            if (self hasnt open && GoalOpen(self)) {
                <<Touch self>>;
            }
        Close:
            if (self has open && GoalOpen(self)) {
                <<Touch self>>;
            }
        Attack, Push, Pull, Receive, LetGo:
            if (GoalOpen(self))
                <<Touch self>>;
    ],
    prereqs stove fridge,
    opentime 0,
    imp [;
        self.opentime++;
        switch (self.opentime) {
            1:
                if (player in kitchen)
                    print "^A faint creaking comes from the kitchen ceiling.^";
                else
                    print "^A faint creaking comes from the direction of the
                        kitchen.^";
            2:
                print "^With a muffled ", (emph) "crack", " the kitchen
                    ceiling gives way. Broken plaster and rotten lath
                    tumble down as you";
                if (player in kitchen)
                    print " leap aside";
                else
                    print " stare";
                print ". And they, of course, are followed by sand, white
                    sand, rivers of sand. In moments the kitchen alcove is
                    lost to your sight.^";
                BumpSand(livingsand);
                RescueContents(kitchen);
                remove kitchen;
                Goaled(kitchen);
                return -1;
        }
    ],
  has scenery container openable ~open;

Object -> -> crackers "box of crackers"
  with
    name 'box' 'of' 'cracker' 'crackers' 'substrate',
    short_name [;
        if (self has open) {
            print "box of sand";
            rtrue;
        }
        rfalse;
    ],
    seenonce false,
    description [;
        if (self has open) {
            "The box is open, but it contains no crackers. Instead, it
                is packed with sand.";
        }
        "You buy this species of bland starchy substrate to
            put underneath your peanut butter.";
    ],
    before [ ix;
        Smell:
            if (OIU(self)) rtrue;
            if (self hasnt open) {
                "It's not a particularly odiferous variety of cracker.";
            }
            rfalse;
        Eat, Taste, Drink:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                <<Open self>>;
            "You can't eat sand.";
        Open:
            if (OIU(self)) rtrue;
            if (self has open)
                "The box is already open.";
            if (~~GoalEverOpen(self))
                "You have no appetite this early in the morning.";
            give self open;
            move boxsand to self;
            Goaled(self);
            ix = FindFloorZone(player);
            if (ix)
                BumpSand(ix);
            print "You pull on the box top. It's stuck, somehow. You 
                yank --^^The top tears away, and white sand sprays out of
                the box.^";
            if (GoalClosed(pbutter))
                print "^This is getting to be too much. You consider
                    which of your friends might have snuck in here and
                    set all of this up. No... mmm... nobody, really.^";
            rtrue;
        Close:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The box is already closed.";
            "The top is gone.";
        Receive:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The box is closed.";
            "You wouldn't want to jam anything into a cracker box.
                Especially not one full of sand.";
        Search:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The box is closed.";
            <<Examine self>>;
        Wave:
            if (OIU(self)) rtrue;
            if (self has open)
                <<Empty self>>;
            if (GoalEverOpen(self))
                "Something shifts around in the box, but it doesn't sound
                    like crackers. Strange.";
            "Some crackers rattle around.";
        Empty, EmptyT:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The box is closed.";
            <<Drop boxsand>>;
    ],
    prereqs vacuum,
  has rescuable ~open transparent;

Object -> fridge "refrigerator"
  with
    name 'fridge' 'refrigerator',
    description [;
        if (GoalClosed(self)) {
            "The fridge hangs open. An unmoving torrent of sand pours
                down onto the floor.";
        }
        print "It's yellow. Beyond that, it looks like every other
            refrigerator in the United States.";
        if (self has open) {
            print " ";
            self.subdescribe();
        }
        new_line;
        rtrue;
    ],
    nosubdescribe [; return (self hasnt open); ],
    subdescribe [ ix;
        if (GoalClosed(self)) {
            print "Sand spills from the open refrigerator.";
            rtrue;
        }
        print "The fridge is open";
        ix = child(self);
        if (~~ix) {
            print " but empty";
        }
        else {
            print "; inside";
            WriteListFrom(ix,
                TERSE_BIT + ENGLISH_BIT + ISARE_BIT + CONCEAL_BIT);
        }
        print ".";
        rtrue;
    ],
    opentime 0,
    before [;
        Search:
            if (GoalClosed(self)) {
                "The fridge is full of sand.";
            }
            self.opentime = 0;
            rfalse;
        Receive:
            if (GoalClosed(self)) {
                "The fridge is clogged with sand.";
            }
            self.opentime = 0;
            rfalse;
        LetGo:
            self.opentime = 0;
            rfalse;
        Open:
            if (OIU(self)) rtrue;
            if (GoalOpen(self)) {
                StopDaemon(self);
                give self open;
                RescueContents(self, kitchen);
                BumpSand(kitchensand);
                Goaled(self);
                "You pull open the refrigerator, and try not to flinch as
                    a wall of sand rushes out and buries your feet.";
            }
            if (self hasnt open) {
                StartDaemon(self);
                self.opentime = 0;
            }
            rfalse;
        Close:
            if (GoalClosed(self)) {
                if (OIU(self)) rtrue;
                "The door is jammed open.";
            }
            rfalse;
        Enter:
            if (GoalClosed(self))
                "The fridge is clogged with sand. You couldn't even
                    stuff a loaf of bread in there, much less yourself.";
            if (self has open)
                "Sixty years ago, you could suffocate yourself by locking
                yourself in the fridge. The miracles of modern technology,
                however, have removed this as a viable option.";
            else
                return L__M(##Insert,3,self);
    ],
    daemon [;
        if (self hasnt open || parent(self) == 0 || GoalClosed(self)) {
            StopDaemon(self);
            return;
        }
        self.opentime++;
        if (self.opentime >= 4) {
            StopDaemon(self);
            give self ~open;
            if (player in kitchen or Apartment or futon or desk)
                print "^The fridge door swings shut.^";
        }
    ],
    prereqs crate closet,
    startup [;
        if (GoalOpen(self)) {
            self.opentime = 10;
        }
    ],
  has scenery container openable ~open;

Object -> -> pbutter "jar of peanut butter"
  with
    name 'jar' 'of' 'peanut' 'butter',
    short_name [;
        if (self has open) {
            print "jar of sand";
            rtrue;
        }
        rfalse;
    ],
    seenonce false,
    description [;
        if (self has open) {
            "The jar is open, but it contains no peanut butter. Instead, it
                is packed with sand.";
        }
        print "Partially hydrogenated. Nutritive, reliable.
            Brandless and brown.";
        if (~~self.seenonce) {
            self.seenonce = true;
            !print " Please don't ask why you keep the peanut butter in the
            !    fridge; the story is long, involute, and beyond the scope
            !    of this game.";
        }
        new_line;
        rtrue;
    ],
    before [ ix;
        Smell:
            if (OIU(self)) rtrue;
            if (self hasnt open) {
                print "You can't smell anything with the jar closed.";
                if (~~GoalEverOpen(self)) 
                    print " You wouldn't find it that appetizing right
                        now, anyway.";
                new_line;
                rtrue;
            }
            rfalse;
        Eat, Taste, Drink:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                <<Open self>>;
            "You can't eat sand.";
        Open:
            if (OIU(self)) rtrue;
            if (self has open)
                "The jar is already open.";
            if (~~GoalEverOpen(self))
                "You have no appetite this early in the morning.";
            give self open;
            move jarsand to self;
            Goaled(self);
            ix = FindFloorZone(player);
            if (ix)
                BumpSand(ix);
            print "You unscrew the lid... with an unexpected grating 
                sound. Something sifts to the floor.^^The peanut butter
                jar is full of sand.^";
            if (GoalClosed(crackers))
                print "^This is getting to be too much. You consider
                    which of your friends might have snuck in here and
                    set all of this up. No... mmm... nobody, really.^";
            rtrue;
        Close:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The jar is already closed.";
            "The lid is gone.";
        Receive:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The jar is closed.";
            "You wouldn't want to jam anything into a peanut butter jar.
                Especially not one full of sand.";
        Search:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The jar is closed.";
            <<Examine self>>;
        Wave:
            if (OIU(self)) rtrue;
            if (self has open)
                <<Empty self>>;
            if (GoalEverOpen(self))
                "Strange. Something shifts inside the jar when you shake it,
                    and cold peanut butter certainly shouldn't.";
            rfalse;
        Empty, EmptyT:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The jar is closed.";
            <<Drop jarsand>>;
    ],
    prereqs vacuum,
    imp [;
        if (~~GoalClosed(crackers))
            return;
        KillImp(vacuum);
        plant.startup(1);
        return -1;
    ],
  has rescuable ~open transparent;

Zone bathroom "bathroom nook"
  with
    name 'bathroom' 'nook' 'alcove',
    description [ num;
        print "The bathroom alcove";
        if (toilet in self)
            print " has a toilet, a sink";
        else
            print " has a sink";
        if (shower in self) {
            print ", and a shower stall that manages to make the
                rest of the place look roomy.";
            print " Not much else; most of your accoutrements have
                been packed.";
        }
        else {
            print ", and a drift of sand where the shower stall used
                to be.";
        }
        new_line;
        num = Locale(self, "You see", "You also see");
        if (num) {
            print " lying";
            if (player in self)
                print " here";
            else
                print " on the bathroom floor";
            print ".^";
        }
        rtrue;
    ],
    nolocifin true,
    locseealso shower,
    before [;
        Examine:
            if (OIU(self)) rtrue;
            rfalse;
        Search:
            <<Examine self>>;
    ],
    prereqs toilet shower;

Object -> toilet "toilet"
  with
    name 'toilet' 'commode' 'wc',
    description "Surely you know what a toilet looks like.",
    before [;
        Enter:
            if (verb_word == 'run')
                <<Empty self>>;
            "You feel no such need at the moment.";
        Search:
            if (OIU(self)) rtrue;
            "Porcelain and mildew glitter damply at you. You're glad
                you looked.";
        Empty:
            if (OIU(self)) rtrue;
            if (GoalOpen(self)) {
                Goaled(self);
                remove self;
                BumpSand(bathroomsand);
                plant.startup(2);
                "The toilet? Yes, it's the toilet's turn! You press the
                    handle, grinning maniacally. And indeed, the sand
                    rushes down the sides of the bowl; the shining
                    porcelain itself crazes, cracks, and veils to the
                    bathroom floor. The toilet is a pedestal, a stump,
                    a mere pile of sand. Gone.";
            }
            "The result is predictable:
                the murky water in the toilet is replaced
                by different murky water.";
            ! In all adventure games that implement a bathroom, surely
            !    there shall be implemented a humorous ~flush~ command.
            !    So it is now. 
        SwitchOn:
            <<Empty self>>;
        EmptyT:
            if (OIU(self)) rtrue;
            "No, thanks. The toilet really isn't all that clean.";
        Open:
            if (OIU(self)) rtrue;
            "It already is.";
        Close:
            if (OIU(self)) rtrue;
            "You're a lid-up sort of person.";
    ],
    receive_water "You empty the glass into the toilet. Glug.",
    receive_sand [;
        print "Do you want to clog the toilet again? No, you do not.^";
        return 2;
    ],
    prereqs crate luggage,
    opentime 0,
    imp [;
        if (~~GoalClosed(shower))
            return;
        self.opentime++;
        switch (self.opentime) {
            1:
                if (player in bathroom)
                    print "^A faint creaking comes from the bathroom ceiling.^";
                else
                    print "^A faint creaking comes from the direction of the
                        bathroom.^";
            2:
                print "^With a muffled ", (emph) "crack", " the bathroom
                    ceiling gives way. Broken plaster and rusty pipes
                    tumble down as you";
                if (player in bathroom)
                    print " leap aside";
                else
                    print " stare";
                print ". And down comes the sand, a dry white rushing river,
                    hissing, shrieking. 
                    In moments the bathroom is lost to your sight.^";
                BumpSand(livingsand);
                RescueContents(bathroom);
                remove bathroom;
                Goaled(bathroom);
                return -1;
        }
    ],
  has scenery;

Sink -> bathsink
  with
    name 'bathroom',
    self_word 'bathroom',
    self_name "bathroom",
    self_obj bathsinkwater,
    seenonce false,
    description [;
        if (GoalClosed(self))
            "The bathroom sink is full of sand.";
        print "The bathroom sink is small and just a bit scummy.";
        if (~~self.seenonce) {
            self.seenonce = true;
            print " (You never quite understood how bathroom sinks acquire
                grunge. After all, most of what passes through them is soap
                and water... It's a mysterious world sometimes.)";
        }
        new_line;
        rtrue;
    ],
    before [;
        SwitchOn, Turn:
            if (OIU(self)) rtrue;
            if (GoalClosed(self))
                "More sand pours from the bathroom sink's faucet.";
            if (GoalOpen(self)) {
                Goaled(self);
                remove self.self_obj;
                self.self_obj = bathsinksand;
                move self.self_obj to self;
                BumpSand(bathroom);
                "With an eerie hiss, dry white sand boils from the faucet.
                    You yank at the tap, but the bathroom sink is already
                    full, and sand spilling on the floor.";
            }
            "You spin the tap. The pipes sound like an indigestive mammoth,
                but water does dribble out of the faucet. Reassured, you
                turn the tap back off.";
    ],
    imp [;
        closet.startup();
        return -1;
    ],
    prereqs pbutter crackers;

SinkWater -> -> bathsinkwater
  with
    name 'bathroom',
    self_word 'bathroom',
    self_name "bathroom",
    before [;
        Insert:
            if (OIU(self)) rtrue;
            if (second ~= glass)
                "That won't hold water.";
            if (glass notin player)
                "You're not holding the glass.";
            if (water in glass)
                "The glass is already full.";
            if (GoalOpen(bathsink)) {
                <<SwitchOn bathsink>>;
            }
            if (sand in glass)
                "Water dribbles into the glass, soaking into the sand.
                    After a few moments, you realize the sand is as
                    dry as when you started. Strange.";
            move water to glass;
            PronounNotice(water);
            "The faucet rattles, and water dribbles into the glass.";
        SwitchOn:
            <<SwitchOn bathsink>>;
    ];

Zone -> shower "shower"
  with
    name 'shower' 'stall' 'curtain',
    leave_fragment " get out of the shower",
    enter_fragment " squeeze into the shower",
    description [ num;
        print "The shower stall is a familiar symphony
            in pinkish-grey tile and grunge. Well, more of a
            solo. A short solo";
        if (player in self)
            print "; you reflexively crane your neck
                to keep from smacking the showerhead";
        print ".^";
        num = Locale(self, "You see", "You also see");
        if (num) {
            print " lying";
            if (player in self)
                print " here";
            else
                print " in the shower stall";
            print ".^";
        }
        rtrue;
    ],
    nolocifin true,
    ! locseealso bathroom,
    before [;
        Examine:
            if (OIU(self)) rtrue;
            rfalse;
        Search:
            <<Examine self>>;
        Take:
            <<SwitchOn self>>;
        Open, Close, Push, Pull:
            if (OIU(self)) rtrue;
            "The shower curtain has become a daily ritual of annoyance.
                It's too small. It cannot, no matter how you tug at it,
                actually cover the shower stall. You've managed to
                arrange the curtain so that 
                the minimal amount of water splashes
                onto the bathroom floor when you shower, and that's
                where you leave it.";
        SwitchOn:
            <<SwitchOn showerhead>>;
        SwitchOff:
            <<SwitchOff showerhead>>;
        Enter:
            if (verb_word == 'run')
                <<SwitchOn showerhead>>;
        EmptyT:
            <<Insert showerheadwater second>>;
    ],
    prereqs crate stove;

Object -> -> showerhead "showerhead"
  with
    name 'showerhead' 'shower' 'head' 'tap' 'faucet',
    parse_name [ wd firstwd num;
        wd = NextWord();
        firstwd = wd;
        while (WordInProperty(wd, self, name)) {
            num++;
            wd = NextWord();
        }
        if (num == 1 && firstwd == 'shower')
            return 0;
        return num;
    ],
    before [;
        EmptyT:
            <<Insert showerheadwater second>>;
        SwitchOn:
            if (OIU(shower)) rtrue;
            if (GoalOpen(shower)) {
                RescueContents(shower, bathroom);
                remove shower;
                BumpSand(bathroom);
                Goaled(shower);
                "You turn the tap, and -- no surprise now -- dry sand floods
                    from the showerhead. Shielding your eyes from the spray,
                    you reach to turn the tap back off.^^A creaking from
                    above warns you. You leap back as the plaster cracks
                    overhead, and whiteness roars down.^^When silence returns,
                    the entire far end of the bathroom is a blank slope of
                    sand.";
            }
            "You turn the tap... and nothing happens. Hmm. The shower
                was working last night. You knew the pipes in your
                apartment were unreliable, but this is worse than usual.";
        SwitchOff:
            if (OIU(shower)) rtrue;
            "The shower isn't running.";
        Enter:
            if (verb_word == 'run')
                <<SwitchOn showerhead>>;
    ],
  has scenery;

GeneralWater -> -> showerheadwater
  with
    name 'shower',
    self_word 'shower',
    self_name "shower",
    before [;
        Insert:
            if (OIU(shower)) rtrue;
            if (second ~= glass)
                "That won't hold water.";
            if (glass notin player)
                "You're not holding the glass.";
            if (water in glass)
                "The glass is already full.";
            <<SwitchOn showerhead>>;
        SwitchOn:
            <<SwitchOn showerhead>>;
        Enter:
            if (verb_word == 'run')
                <<SwitchOn showerhead>>;
    ];

Object glass "glass"
  with
    name 'glass',
    invent [;
        if (inventory_stage == 1)
            rfalse;
        if (water in self)
            print " of water";
        if (sand in self)
            print " of sand";
        rtrue;
    ],
    partinvent [;
        return self.invent();
    ],
    description [;
        print "An ordinary drinking glass";
        if (water in self)
            print ", full of water";
        if (sand in self)
            print ", full of sand";
        ".";
    ],
    before [ ix;
        Search:
            if (water in self)
                "The glass contains water.";
            if (sand in self)
                "The glass contains sand.";
            "The glass is empty.";
        Fill:
            if (self notin player)
                "You're not holding the glass.";
            ix = 0;
            if (TestScope(bathsinkwater))
                ix = bathsinkwater;
            if (ix == 0 && TestScope(bathsinksand))
                ix = bathsinksand;
            if (ix == 0)
                "You'll have to say what to fill the glass with.";
            if (OIU(ix)) rtrue;
            <<Insert ix self>>;
        Receive:
            "That sort of juggling is unlikely to help.";
        Empty:
            if (OIU(self)) rtrue;
            ix = child(self);
            if (ix)
                <<Drop ix>>;
            "The glass is already empty.";
        EmptyT:
            if (OIU(self)) rtrue;
            ix = child(self);
            if (ix)
                <<Insert ix second>>;
            "The glass is already empty.";
        Drink:
            if (OIU(self)) rtrue;
            ix = child(self);
            if (~~ix)
                "The glass is empty.";
            <<Drink ix>>;
        BlowOn:
            if (OIU(self)) rtrue;
            ix = child(self);
            if (ix)
                <<BlowOn ix>>;
    ],
    receive_water [;
        print "The glass is already full.^";
        return 2;
    ],
    receive_sand [;
        print "The glass is already full.^";
        return 2;
    ],
  has rescuable container open;

Object water "glassful of water"
  with
    name 'glassful' 'of' 'water',
    description "The water glints like a mirage.",
    seenonce 0,
    before [ ix;
        Take:
            "With your bare hands?";
        BlowOn:
            if (OIU(self)) rtrue;
            "The water ripples gently.";
        Drop:
            if (~~IndirectlyContains(player, self))
                "You haven't got that.";
            remove self;
            ix = FindFloorZone(player);
            if (ix)
                ix = ix.sand_obj;
            print "The water splashes";
            if (ix == 0 || ix.number <= 1)
                print " onto the floor";
            else {
                if (ix.number <= 4)
                    print " onto the sand on the floor";
                else
                    print " into the sand";
            }
            print ", leaving a wet spot that dries in seconds";
            self.seenonce++;
            if (self.seenonce == 2) 
                print ". Hmm";
            ".";
        EmptyT:
            <<Insert self second>>;
        Insert, PutOn:
            if (~~IndirectlyContains(player, self))
                "You haven't got that.";
            if (second provides receive_water) {
                if (OIU(second)) rtrue;
                ix = second.receive_water();
                if (ix) {
                    if (ix ~= 2)
                        remove self;
                    rtrue;
                }
            }
            if (second == d_obj)
                <<Drop self>>;
            "Water will do that no good.";
        Taste:
            if (OIU(self)) rtrue;
            "The water is warm and flat-tasting.";
        Drink:
            if (OIU(self)) rtrue;
            remove self;
            print "You gulp the water. It feels vaguely astringent in your 
                mouth, unsatisfying.";
            if (GoalOpen(self)) {
                print " Well, no doubt thirst will be more real after you
                    fly to California --";
                print "";
                Goaled(self);
                KillImp(firstgoal); ! thirst imp
            }
            new_line;
            rtrue;
    ],
    opentime 0,
    imp [;
        self.opentime++;
        switch (self.opentime) {
            2: print "^-- Fly? Where did you leave your plane tickets?^";
            3: print "^You hate this feeling. It happens ", (emph) "all
                the time.", " Something safely accomplished, and then pow, 
                you have to worry about it again. Unfair! The tickets must
                be here somewhere.^";
                return -1;
            4: print "^[BUG] water imp^";
        }
    ],
    prereqs firstgoal,
  has scenery;

! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


[ LivingLookNooks;
    if (kitchen in Apartment && bathroom in Apartment) {
        print " A kitchen nook one way and a bathroom nook the
            other, with a closet to the side.";
        return;
    }
    
    if (bathroom in Apartment) {
        print " A bathroom nook and closet are to the side.";
        return;
    }
    
    if (kitchen in Apartment) {
        print " A kitchen nook one way and a closet the other.";
        return;
    }
    
    print " A closet to one side.";
];

Object Apartment "apartment"
  with
    articles "Your" "your" "your",
    seenonce false,
    description [ ix;
        if (GoalClosed(window)) {
            print "Sunlight cuts across the room like hot butter, blazing
                from the window. The room is nearly barren now;
                walls and ceiling are blank, but for the window, the
                closed front door, and the full-length mirror across
                from them.^^";
            print "The futon sits alone in the center of the room.
                Next to it, askew on a pile of sand, rests the faithful
                old stereo.^";
            DescribeParagraph(self);
            rtrue;
        }
        ix = FindZone(player);
        switch (ix) {
            futon, Apartment:
                if (~~self.seenonce) {
                    new_line;
                    print "Odd, how the light just makes your apartment
                        gloomier. Pre-dawn darkness pools in the corners
                        and around the tops of walls. Your desk lamp glares
                        yellow, but the shadows only draw your eyes and 
                        deepen.^^";
                    print "Not much of an apartment, no.";
                }
                else {
                    print "You survey your one small room.";
                }
                if (desk in Apartment)
                    print " One desk, paper-piled, with a dusty computer
                        shoved to the side.";
                print " Your futon";
                if (player in futon && self.seenonce)
                    print ", upon which you sit";
                print ".";
                print " Second-hand stereo";
                if (crate in Apartment)
                    print " sitting on a cardboard crate";
                print ".";
                LivingLookNooks();
            kitchen:
                print "You survey your one small room. ";
                kitchen.description();
                print "^The rest of the place is mostly filled by your
                    futon";
                if (desk in Apartment)
                    print ", and the computer desk in the corner";
                print ".";
                if (bathroom in Apartment)
                    print " The bathroom alcove is across from you,
                        and the closet next to it.";
                else
                    print " The closet is across from you.";
                print " The stereo sits on";
                if (crate in Apartment)
                    print " a cardboard crate";
                else
                    print " the sandy floor";
                print ".";
            bathroom, shower:
                if (player in shower) {
                    shower.description();
                    print "^Beyond the curtain, your apartment is";
                }
                else {
                    print "You survey your one small room. ";
                    bathroom.description();
                    print "^The rest of the place is";
                }
                print " mostly filled by your futon";
                if (desk in Apartment)
                    print ", and the computer desk in the corner";
                print ".";
                if (kitchen in Apartment)
                    print " The kitchen alcove is across from you,
                        and the closet just to one side.";
                else
                    print " The closet is just to one side.";
                print " The stereo sits on";
                if (crate in Apartment)
                    print " a cardboard crate";
                else
                    print " the sandy floor";
                print ".";
            desk:
                print "You glance around. ";
                desk.description();
                print "^Behind you, the futon takes up most of the apartment.";
                print " A second-hand stereo sits on";
                if (crate in Apartment)
                    print " a cardboard crate";
                else
                    print " the sandy floor";
                print ".";
                LivingLookNooks();
            default:
                print "[BUG] Player not in zone.";
        }
        print " A broad mirror tries to make the place seem twice its
            size; it halfway works.";
        print " One window, whose shade is";
        if (windowshade hasnt open)
            print " down";
        else
            print " up";
        print ", and the front door";
        if (frontdoor hasnt open)
            print " firmly shut";
        else
            print " wide open";
        print ".^";
        if (self.seenonce) {
            if (desk in Apartment) {
                print "^The place is still too dim, except the corner where
                    the desk lamp is somehow too bright.^";
            }
            else {
                print "^The room is very dim, now. Blackness
                    creeps around the windowshade.^";
            }
        }
        DescribeParagraph(self);

        if (~~self.seenonce) {
            print "^You are sprawled on the futon, staring up into
                that gloom. Your eyes feel gritty. But it's too late
                -- early -- no time left for sleep, anyway. In a few
                hours your ride will arrive.^";
            self.seenonce = true;
        }
        rtrue;
    ],
    before [;
        Go: 
            "You paced enough last night.";
        Exit:
            if (player notin self)
                rfalse;
            <<Enter frontdoor>>;
        Listen:
            if (noun == 0) {
                if (motornoise in self) {
                    if (stereo in self && stereo has on) {
                        <Listen stereo>;
                        new_line;
                    }
                    <<Listen motornoise>>;
                }
                if (stereo in self)
                    <<Listen stereo>>;
            }
            else if (noun == u_obj) {
                if (GoalOpen(kitchen) || GoalOpen(bathroom))
                    "The creaking grows louder.";
            }
        Examine:
            switch (noun) {
                d_obj:
                    if (GoalClosed(tickets))
                        "It's a bit sandy.";
                    else
                        "It's a bit dusty.";
            }
            rfalse;
    ],
    locseealso kitchen bathroom,
    sand_obj 0,
    leave_fragment " [BUG] step out into hyperspace",
    enter_fragment " [BUG] fall out of hyperspace";

Object -> apartmentproxy "living room"
  with
    name 'living' 'main' 'room' 'apartment',
    before [;
        Examine, Search:
            <<Look>>;
        Enter:
            if (player in Apartment)
                "But you're already in the living room.";
            MoveToZone(Apartment, 1);
            rtrue;
        Receive:
            <<Drop noun>>;
    ],
  has scenery;

Object -> crate "cardboard crate"
  with
    name 'cardboard' 'carton' 'crate',
    description "The crate your computer arrived in. It's dented and
        disreputable now, but still serves the time-honored purpose
        of holding your stereo up.",
    before [;
        Take:
            if (GoalOpen(self))
                <<Touch self>>;
            "The cardboard crate is useful where it is.";
        Open, Search:
            if (OIU(self)) rtrue;
            if (GoalOpen(self))
                <<Touch self>>;
            "You know it's empty.";
        Close:
            if (OIU(self)) rtrue;
            if (GoalOpen(self))
                <<Touch self>>;
            "The cardboard crate is already closed.";
        Attack:
            if (OIU(self)) rtrue;
            if (GoalOpen(self))
                <<Touch self>>;
            "You produce a pleasing thumpty sound.";
        Touch:
            if (GoalOpen(self)) {
                Goaled(self);
                remove self;
                BumpSand(livingsand);
                "You barely touch it, and the heavy cardboard abruptly 
                    tears. Sand pours out across the living room floor.
                    The stereo sags; in moments, the crate is reduced
                    to paper rags, which are immediately buried in the 
                    sand.";
            }
            rfalse;
        Rub, Push, Pull:
            if (GoalOpen(self))
                <<Touch self>>;
            rfalse;
    ],
    prereqs [; return (GoalClosed(bathsink) || GoalClosed(kitchensink)); ],
    imp [; fridge.startup(); return -1; ],
  has scenery;

Global stereosmartcount = 0;

Object -> stereo "stereo"
  with
    name 'stereo' 'radio',
    description [;
        print "The stereo is old, dating from the era when matte-black,
            featureless, and opaque were the watchwords of hipness.
            It still works, however";
        if (self has on) {
            print ", as the strains of ";
            self.song();
            print " demonstrate";
        }
        ".";
    ],
    before [;
        Listen:
            if (self hasnt on)
                "The stereo is off.";
            print "You are listening to ";
            self.song();
            ".";
        LookUnder:
            if (crate in Apartment) {
                "The stereo is resting on a cardboard crate.";
            }
            rfalse;
    ],
    after [;
        SwitchOn:
            self.number = random(32761);
            StartDaemon(self);
            print "The stereo buzzes, hunts, and then locates its station.";
            if (~~self.seenonce) {
                self.seenonce = true;
                print " Ah, your favorite small-time indy worldbeat/techno
                    station. At least you think that's what they call it.
                    It's a pretty stream-of-consciousness format.^^";
            }
            else {
                print " ";
            }
            print "The room fills with the sound of ";
            self.song();
            ".";
        SwitchOff:
            "You turn off the stereo.";
    ],
    startup [;
        if (self hasnt on) {
            self.number = random(32761);
            print "^Suddenly the room fills with the sound of ";
            self.song();
            print ". What? Oh, yes -- you jiggered the stereo timer
                to act as an alarm clock, back when you still thought
                sleep was possible. Terrific. You've arrived at the
                morning, although the world outside is still dark.
                The taxi should be showing up soon.";
            print "^^Almost immediately, the hourly news comes on.^";
            give self on;
            StartDaemon(self);
        }
        else {
            print "^The stereo clicks as ";
            self.song();
            print " ends and the hourly news comes on. What?
                Morning must have arrived,
                although the world outside is still dark.
                The taxi should be showing up soon.^";
        }
        self.number = -1;
        self.opentime = 0;
    ],
    seenonce false,
    number 0,
    opentime 2,
    longcount 0,
    daemon [;
        if (self hasnt on || parent(self) == 0) {
            StopDaemon(self);
            return;
        }
        if (self.number == -1) {
            self.opentime++;
            switch (self.opentime) {
                2: print "^~More allegations of campaign finance
                    misconduct...~ (Oh, fascinating news day.)^";
                3: print "^~Sharp words between the superpowers
                    today...~ (Huh? There are still superpowers?)^";
                4: print "^~Three people reported missing at the Death
                    Valley Om in California...~ (Hang on.) ~The
                    popular desert arts festival was shaken yesterday
                    when three attendees failed to return from moonlight
                    hikes. State police are searching the area.~ (You
                    resolve to read that desert camping book one more
                    time.)^";
                5: print "^~In the business pages, stocks are down...~
                    (You once again lose interest.)^";
                6:
                    self.number = random(32761);
                    self.opentime = 0;
                    print "^~And that's, um... far out. Back to the groove,
                        folks,~ and ";
                    self.song();
                    print " begins.^";
            }
            rtrue;
        }
        self.opentime++;
        if (self.opentime >= random(3) + 3) {
            self.opentime = 0;
            self.number = random(32761);
            self.longcount++;
            if (self.longcount >= 15) {
                self.longcount = 0;
                print "^The radio mumbles, ~This is, um, one-oh-three...
                    kay-tee-oh-whoa... your source for interworldbeat...~
                    You can practically hear the bong gurgling
                    in the background. After a moment, ";
                self.song();
                print " begins playing.^";
            }
            else {
                print "^The radio begins playing ";
                self.song();
                print ".^";
            }
        }
    ],
    song [ num redundant;
        redundant = 0;
        num = self.number;
        if (num == -1) {
            print "the news";
            rtrue;
        }
        if (num % 97 > 31) {
            switch (num % 13) {
                0: print "an Afro-"; redundant++;
                1: print "an Indo-";
                2: print "a reggae-";
                3: print "an Antarcto-";
                4: print "a Sino-";
                5: print "a Filipo-";
                6: print "a Hispano-";
                7: print "a Balto-";
                8: print "a polko-";
                9: print "a hypno-";
                10: print "an Austro-";
                11: print "a Franco-";
                12: print "an Appalacho-";
            }
        }
        else {
            switch (num % 13) {
                0: print "a haunting";
                1: print "a creepy";
                2: print "a jazzy";
                3: print "a thumping";
                4: print "a jittery";
                5: print "an electrifying";
                6: print "a soothing";
                7: print "a calm";
                8: print "an angry";
                9: print "a vibrant";
                10: print "a bouncy";
                11: print "a rhythmic";
                12: print "a repetitive";
            }
            print " ";
        }
        switch (num % 19) {
            0: print "Manx";
            1: print "Celtic";
            2: print "Korean";
            3: print "Japanese";
            4: print "Javanese";
            5: print "Balinese";
            6: print "Slavic";
            7: print "Hmong";
            8: print "Basque";
            9: print "Andean";
            10: print "Nigerian"; redundant++;
            11: print "electronic";
            12: print "Sumerian";
            13: print "Atlantean (?)";
            14: print "Inuit";
            15: print "Estonian";
            16: print "Maori"; redundant++;
            17: print "Shaker";
            18: print "Dominican";
        }
        if (redundant==2) {
            stereosmartcount++;
            if (stereosmartcount == 1)
                print " (okay, it's redundant)";
        }
        print " ";
        switch (num % 11) {
            0: print "lullaby";
            1: print "chant";
            2: print "fugue";
            3, 4, 5: print "song";
            6, 7: print "melody";
            8, 9, 10: print "tune";
        }
    ],
  has scenery switchable ~on;

Object -> luggage "luggage"
  with
    name 'luggage' 'suitcase' 'suitcases',
    description [;
        print "You've packed and repacked: breakdown tent, dried food,
            sunscreen, compass, mylar space-blanket,
            long underwear and showoff tats,
            walking shoes and dancing shoes and hiking shoes. You're
            ready for the Death Valley Om.^";
        BackgroundInfo(self, "As ready as you're likely to get.");
        if (GoalOpen(tickets)) {
            if (self.ticket_search == 0) {
                self.ticket_search = 1;
                print "^Oh dear. You didn't pack the tickets in with your
                    luggage, did you?^";
            }
        }
    ],
    ticket_search 0,
    background_flag false,
    subdescribe "Your luggage is piled untidily by the door.",
    before [;
        Open, Search:
            if (OIU(self)) rtrue;
            if (GoalOpen(self)) {
                Goaled(self);
                remove self;
                BumpSand(livingsand);
                "You grab a suitcase handle -- which crumbles dryly in
                    your grip. In seconds and in silence, the whole row
                    of suitcases and duffels has collapsed to white sand
                    on the living room floor.";
            }
            if (GoalOpen(tickets)) {
                print "You quickly pop open each suitcase and riffle
                    through, trying not to disarrange your tidy packing
                    too badly. ";
                CheckTicket(self);
                rtrue;
            }
            "Oh, no. You spent hours jamming everything into the maximally
                packed configuration. If you disturb the luggage now,
                you fear, you'll still be repacking when the taxi arrives.";
        Close:
            if (OIU(self)) rtrue;
            "All the luggage is already closed.";
        Push, Pull, Take:
            if (OIU(self)) rtrue;
            if (GoalOpen(self))
                <<Open self>>;
            "That's hardly portable -- and I do mean ", (emph) "hardly.";
        Touch:
            if (GoalOpen(self))
                <<Open self>>;
        Empty, EmptyT:
            <<Open self>>;
    ],
    prereqs closet stove,
  has scenery;

Object -> plant "plant"
  with
    name 'pot' 'potted' 'plant' 'soil',
    short_name [;
        switch (self.number) {
            0: print "hyacinth";
            1: print "spider plant";
            2: print "palm plant";
            3: print "cactus";
            default: print "[BUG]";
        }
        rtrue;
    ],
    parse_name [ wd num altwd;
        switch (self.number) {
            0: altwd = 'hyacinth';
            1: altwd = 'spider';
            2: altwd = 'palm';
            3: altwd = 'cactus';
            default: altwd = 'plant';
        }
        wd = NextWord();
        while (WordInProperty(wd, self, name) || wd == altwd) {
            num++;
            wd = NextWord();
        }
        return num;
    ],
    subdescribe [;
        print "A potted ";
        self.short_name();
        print " sits beneath the window.";
        rtrue;
    ],
    number 0, ! 0: hyacinth; 1: spider plant; 2: palm; 3: cactus
    startup [ level;
        if (self.number >= level)
            return;
        self.number = level;
        self.waterlevel = 0;
    ],
    waterlevel 1,
    description [;
        print "This plant is your one claim to actual responsibility.
            The ";
        self.short_name();
        print " sits by the window, soaking up sunlight (when
            it's not night) and managing to thrive despite your lack of
            gardening skill.";
        switch (self.waterlevel) {
            1:
                print " Although it looks a little parched at the moment.";
            0:
                print " Although it looks very dry.";
        }
        new_line;
        rtrue;
    ],
    before [;
        Push, Pull, Take:
            if (OIU(self)) rtrue;
            "The plant is where it belongs.";
        Touch:
            if (OIU(self)) rtrue;
            switch (self.waterlevel) {
                1: "The soil feels a bit dry.";
                0: "The soil feels very dry.";
                2: "The soil feels nicely moist.";
                3: "The soil feels very moist.";
            }
        Attack:
            if (OIU(self)) rtrue;
            "The plant has enough problems.";
    ],
    receive_water [;
        if (self.number > 0) {
            "As you carry the glass over to ", (the) self, ", your
                foot slips. (You ", (emph) "really", " have to get
                that sand cleaned up.) The water spills everywhere
                but into the pot.";
        }
        if (self.waterlevel >= 3) {
            print "If you water it any more, it'll get root rot.^";
            return 2;
        }
        self.waterlevel++;
        "You water the plant.";
    ],
  has scenery;

Object -> mirror "mirror"
  with
    name 'full' 'length' 'broad' 'mirror' 'reflection',
    description [;
        print "The far side of the mirror is just as";
        if (GoalClosed(window))
            print " bright";
        else
            print " shadowy";
        " as this one.
            It's probably meant as commentary; not more space, just
            more of the same.";
    ],
    before [;
        Search, Examine:
            if (OIU(self)) rtrue;
            if (GoalOpen(self))
                <<Enter self>>;
            self.description();
            rtrue;
        Touch:
            if (OIU(self)) rtrue;
            if (GoalOpen(self))
                <<Enter self>>;
            "You reach out and touch your hand to itself. It feels
                like glass.";
        Pull, Push, Take:
            if (OIU(self)) rtrue;
            "The mirror is firmly anchored to the wall.";
        Attack:
            if (OIU(self)) rtrue;
            "Seven years' bad luck ", (emph) "and", " a mess to clean
                up. You refrain.";
        Enter:
            if (OIU(self)) rtrue;
            if (GoalOpen(self)) {
                print "You look into the mirror. The far side is just as 
                    bright as this one. With the sunlight behind you, only
                    your own face is in shadow -- the walls, the door, 
                    your furniture, are all full-lit. Beyond your
                    reflection and the window-frame, the 
                    street outside, the houses and trees, are all --^^";
                print (emph) "The street outside?";
                print "^^You spin around.^^";
                KeyPause();
                print "^^^";
                while (child(player)) {
                    move child(player) to Apartment;
                }
                PlayerTo(FakeApartment);
                give stereo ~on;
                Goaled(self);
                rtrue;
            }
            "Popping in and out of mirrors, now? Wonderland isn't so
                close at hand.";
    ],
    prereqs window,
  has scenery;

Object -> frontdoor "front door"
  with
    name 'front' 'door',
    description "The front door is white and rectangular -- yes. The
        door is closed.",
    seenonce false,
    before [;
        Open:
            if (OIU(self)) rtrue;
            if (GoalEverOpen(window)) {
                if (~~self.seenonce) {
                    self.seenonce = true;
                    desk.opentime = 9; ! no honk for a couple of turns
                    print "You open the door.^^Outside is full night. You can
                        see little... nothing. No taxi, no stars, not even
                        streetlights. The world ends at your
                        doorstep.^^^";
                    print (emph) "Quietly, the night begins to
                        enter.";
                    "^^^You are standing with your back to the closed
                        door. You close your eyes, then tear them open;
                        darkness is worse than the sand. Not so funny
                        any more... you concentrate on slowing your breath.";
                }
                else {
                    "You do not want that.";
                }
            }
            "The sun hasn't risen; what light you have would just leak
                out into the night. Anyway, the taxi hasn't arrived,
                so there's nowhere to go.";
        Search:
            if (OIU(self)) rtrue;
            "The door is closed.";
        Close:
            if (OIU(self)) rtrue;
            "The door is already closed.";
        Enter:
            if (OIU(self)) rtrue;
            if (GoalEverOpen(window))
                <<Open self>>;
            "The taxi isn't here yet.";
    ],
  has scenery;

Object -> window "window"
  with
    name 'window',
    description [;
        if (windowshade hasnt open) {
            "The shade is drawn.";
        }
        "Outside, dunes roll away to the rising sun. Nothing else is
            visible. Only the sunlight that burns you, full in
            the face. And sand.";
    ],
    before [;
        Examine, Search:
            self.description();
            rtrue;
        Attack:
            if (OIU(self)) rtrue;
            "You'd lose your security deposit. Also your air conditioning.";
        Open, Close:
            if (OIU(self)) rtrue;
            "The window-glass is sealed; it can't open or close. Some
                notion of thermal efficiency. Truthfully, you like it
                that way.";
        Enter:
            "You can't crawl through glass.";
        LookUnder:
            if (OIU(self)) rtrue;
            "Someone has labelled that space, in ballpoint pen.";
    ],
    prereqs kitchen bathroom desk,
  has scenery transparent;

Object -> -> windowshade "windowshade"
  with
    name 'window' 'shade' 'windowshade' 'blind' 'blinds',
    parse_name [ wd firstwd num;
        wd = NextWord();
        firstwd = wd;
        while (WordInProperty(wd, self, name)) {
            num++;
            wd = NextWord();
        }
        if (num == 1 && firstwd == 'window')
            return 0;
        return num;
    ],
    description [;
        print "The shade is a white plastic sheet -- more yellowing, maybe.
            One of those roller mechanisms that never quite works.";
        if (self hasnt open)
            " The shade is pulled down against the pre-dawn gloom.";
        else
            " The shade is rolled all the way up.";
    ],
    before [;
        Search:
            if (self hasnt open)
                "The windowshade is opaque.";
            <<Examine window>>;
        LookUnder:
            if (self hasnt open)
                <<Open self>>;
            <<Examine window>>;
        Pull, Turn:
            if (self hasnt open)
                <<Open self>>;
            else
                <<Close self>>;
        Enter:
            <<Enter window>>;
        Open, Raise:
            if (OIU(self)) rtrue;
            if (GoalOpen(window)) {
                KillImp(desk);
                frontdoor.seenonce = true;
                give self open;
                BumpSand(livingsand);
                plant.startup(3);
                RescueContents(closet);
                remove closet;
                Apartment.seenonce = 2;
                Goaled(window);
                print "And if you see no taxi outside? What then? ...but the
                    angry honking comes again. You yank hard on the 
                    windowshade, and it whips up as violently.^^^";
                print (emph) "Blazing daylight floods the room.";
                new_line;
                rtrue;
            }
            if (self has open)
                "The shade is already up.";
            "Darkness is already crawling around the edges of the windowshade.
                You have no desire to look night in the face.";
        Close, Lower:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The shade is already down.";
            "But what if the night returns?";
    ],
  has scenery ~open;

Object -> closet "closet"
  with
    name 'closet' 'recess',
    parse_name [ wd num altwd;
        if (GoalOpen(self))
            altwd = 'door';
        else
            altwd = 'closet';
        wd = NextWord();
        while (WordInProperty(wd, self, name) || wd == altwd) {
            num++;
            wd = NextWord();
        }
        return num;
    ],
    seenonce 0,
    description [ ix firsttime;
        print "The closet";
        if (self.seenonce == 0) {
            self.seenonce = 1;
            firsttime = 1;
            print " (if you can call it that)";
        }
        print " is a shallow recess";
        if (bathroom in Apartment)
            print " next to the bathroom";
        else
            print " next to, er, where the bathroom used to be";
        print ".";
        if (self hasnt open) {
            if (~~(GoalOpen(self)))
                print " [BUG:]";
            print " It's closed.^";
            if (self.seenonce < 2) {
                self.seenonce = 2;
                print "^Wait. Didn't the closet lack a door when you
                    moved in?^";
            }
            rtrue;
        }
        ix = child(self);
        if (~~ix) {
            if (GoalClosed(self))
                print " The closet is half-full of sand, but otherwise
                    empty.";
            else
                print " The closet is empty.";
        }
        else {
            if (GoalClosed(self)) {
                print " Peering into the depths, you see deep-drifted sand,
                    and also ";
            }
            else {
                print " Peering into the";
                if (firsttime)
                    print " (ahem)";
                print " depths, you see ";
            }
            WriteListFrom(ix,
                TERSE_BIT + ENGLISH_BIT + CONCEAL_BIT);
            print ".";
        }
        new_line;
        rtrue;
    ],
    before [;
        Search, Examine:
            if (OIU(self)) rtrue;
            self.description();
            rtrue;
        Enter:
            if (self hasnt open)
                "The closet is closed.";
            "It's not a walk-in closet. It's barely even a reach-in closet.";
        Open:
            if (OIU(self)) rtrue;
            if (GoalClosed(self))
                "The closet will certainly never close again.";
            if (~~GoalOpen(self))
                "The closet has no door. It might have once, if those are
                    hinge-marks on the frame, but that must have been several
                    tenants and a dozen coats of paint ago.";
            Goaled(self);
            give self open;
            BumpSand(livingsand);
            "You reach for the knob; then hesitate; then reach again.^^You
                might as well not have bothered. The closet door bursts off
                its hinges before you can touch it. You leap back, barely
                avoiding the cracking plywood as it smashes to the floor,
                buried in the torrent of sand that is pouring out of the
                closet.^^After a few seconds, the room is still again.";
        Close:
            if (OIU(self)) rtrue;
            if (GoalClosed(self))
                <<Open self>>;
            if (~~GoalOpen(self))
                <<Open self>>;
            "The closet is already closed.";
    ],
    prereqs [; return (GoalClosed(bathsink) || GoalClosed(kitchensink)); ],
    imp [; fridge.startup(); return -1; ],
    startup [;
        if (GoalOpen(self) && self has open)
            give self ~open;
    ],
  has scenery container open;

Object -> -> jacket "old jacket"
  with
    name 'old' 'jacket' 'coat' 'xoron',
    article "an",
    description [;
        print "Your jacket hangs in the closet. This is, of course,
            your ", (emph) "old", " jacket -- vaguely waterproof but
            definitely ratty -- suitable only for errands around the
            city.";
        if (GoalClosed(luggage)) {
            new_line;
            rtrue;
        }
        print " Your ", (emph) "new", " jacket, windproof weatherproof
            sandproof Xoron(tm), suitable for desert hiking, is neatly
            packed away in your luggage.^";
        BackgroundInfo(self, "Not that luggage is the important part.");
        if (GoalOpen(tickets)) {
            if (self.ticket_search == 0) {
                self.ticket_search = 1;
                print "^Now, you were wearing this jacket when you picked
                    up the plane tickets...^";
            }
        }
    ],
    ticket_search 0,
    background_flag false,
    before [;
        Take, Wear:
            if (OIU(self)) rtrue;
            "You don't need your old jacket.";
        Search:
            if (OIU(self)) rtrue;
            if (GoalOpen(tickets)) {
                print "You root through the jacket's myriad pockets. ";
                CheckTicket(self);
                rtrue;
            }
            "It's just an old jacket.";
    ],
  has static;

Object -> -> vacuum "vacuum cleaner"
  with
    name 'luggable' 'vacuum' 'cleaner' 'innard' 'innards',
    description [;
        print "The vacuum cleaner is a ~luggable~ model -- meaning
            that it's awkward to carry, but too small to be really
            efficient. Fortunately, you don't generate a lot of dust.";
        if (self has open) {
            print "^^The vacuum cleaner is open, revealing vacuum cleaner
                innards.";
        }
        new_line;
        rtrue;
    ],
    before [ ix jx zowd;
        SwitchOn, Squeeze:
            if (OIU(self)) rtrue;
            ix = FindZone(player);
            if (ix && ix has supporter)
                "You can't use the vacuum cleaner while sitting.";
            jx = parent(self);
            if (~~(jx ofclass Zone || jx == Apartment || jx == player)) {
                print "The vacuum cleaner won't work usefully";
                if (jx has supporter)
                    print " on ", (the) jx;
                else
                    print " in ", (the) jx;
                ".";
            }
            switch (ix) {
                kitchen: zowd = " kitchen";
                bathroom: zowd = " bathroom";
                shower: zowd = " stall";
                default: zowd = "";
            }
            if (self has open) {
                print "The vacuum cleaner can't be started while it's open.";
                if (self.number)
                    print " Not that it looks likely to work in any case,
                        now.";
                new_line;
                rtrue;
            }
            if (self.number >= 5) {
                if (self.number >= 10)
                    "You squeeze the handle. Nothing happens. It's dead.";
                self.number = 10;
                Goaled(self);
                "You squeeze the handle. The vacuum cleaner makes an
                    unpleasant grinding noise, and then stops entirely.
                    The sand must have been too much for it.";
            }
            print "You squeeze the handle, and the vacuum cleaner begins
                humming";
            if (self.number < 2)
                print " industriously";
            else
                print ", albeit with a bit of a whine";
            print ". You run it back and forth on the",
                (string) zowd, " floor";
            if (GoalOpen(self) || GoalClosed(self)) {
                self.number++;
                if (ix && ix.sand_obj && ix.sand_obj.number > 0) {
                    if (ix.sand_obj.number < 3)
                        BumpSand(ix.sand_obj);
                    ". But it only seems to spread the sand around.
                        In fact, it looks like there's more sand than
                        before you started.";
                }
                else {
                    if (ix)
                        BumpSand(ix);
                    ". But after a few strokes, you hear an odd scraping
                        sound.";
                }
            }
            print " for a few moments. It doesn't
                make much difference, but you feel tidy.^";
            rtrue;
        SwitchOff:
            if (OIU(self)) rtrue;
            "This vacuum cleaner turns itself off automatically.";
        Open:
            if (OIU(self)) rtrue;
            if (self has open)
                "It's already open.";
            give self open;
            if (self.number) {
                self.number = 10;
                Goaled(self);
                ix = FindFloorZone(player);
                if (ix)
                    BumpSand(ix);
                "You pop open the vacuum cleaner's casing. Sand spills
                    out -- all over the floor. What the hell?";
            }
            "You pop open the vacuum cleaner's casing. Inside are, well,
                vacuum cleaner innards. You've never really understood
                what goes on in there, but it doesn't look any different
                this time.";
        Close:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "It's already closed.";
            if (self.number) {
                "You try to close the casing, but sand has worked into the
                    joints. You can't get it fully closed, even when
                    you whack it.";
            }
            give self ~open;
            "You close the vacuum cleaner up again.";
        Examine:
            if (self has open && self.number)
                <<Search self>>;
        Search:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The vacuum cleaner is closed.";
            if (self.number) {
                print "The vacuum cleaner's innards are packed with sand.";
                if (~~self.seenonce) {
                    self.seenonce = true;
                    print " You can't imagine how it got that way.";
                }
                new_line;
                rtrue;
            }
            "Inside are, well,
                vacuum cleaner innards. You've never really understood
                what goes on in there, but it doesn't look any different
                this time.";
        Receive:
            if (self hasnt open)
                "The vacuum cleaner is closed.";
            "There's no empty space in the vacuum cleaner.";
        Wave:
            if (self has open && self.number)
                <<Empty self>>;
        Empty, EmptyT:
            if (OIU(self)) rtrue;
            if (self hasnt open)
                "The vacuum cleaner is closed.";
            if (self.number) {
                ix = FindFloorZone(player);
                if (ix && ix.sand_obj == 0)
                    BumpSand(ix);
                "Some more sand sifts down out of the vacuum cleaner.";
            }
            "The vacuum cleaner's innards are unremovable.";
        Enter:
            if (verb_word == 'run')
                <<SwitchOn self>>;
    ],
    seenonce false,
    after [;
        Take:
            "Taken. (Awkwardly.)";
    ],
    number 0,
    prereqs tickets,
    opentime 0,
    imp [;
        self.opentime++;
        switch (self.opentime) {
            1: stereo.startup();
            5: print "^It must be morning, after all -- your metabolism
                    is starting to ratchet up to daytime levels.^";
            6: print "^You're definitely getting hungry.^";
            11: print "^Your stomach rumbles.^";
            18: print "^You consider the available food options.^";
            23: self.opentime = 9;
        }
    ],
  has rescuable ~open;    

Zone futon "futon"
  with
    name 'futon',
    articles "Your" "your" "your",
    description [ ix;
        print "The futon is definitely on the downhill side of life's rolling
            knolls. It serves as both couch and bed, if inadequately in 
            each case, which makes it the most important piece of
            furniture you own.^";
        ix = children(self);
        if ((player notin self && ix > 0) || ix > 1) {
            print "^Sitting on the futon";
            if (player in self)
                print " next to you";
            WriteListFrom(child(self),
                TERSE_BIT + ENGLISH_BIT + ISARE_BIT + CONCEAL_BIT);
            print ".^";
        }
        rtrue;
    ],
    leave_barrier "You won't accomplish much while slumped on the futon.
        That's been getting slowly more apparent for hours now.",
    leave_fragment " stand up",
    enter_fragment [;
        if (random(101) < 40)
            print " flop down on the futon";
        else
            print " sit down";
        rtrue;
    ],
    locseealso kitchen bathroom,
    before [;
        LookUnder:
            if (OIU(self)) rtrue;
            if (player in self)
                print "You twist yourself head-down, and";
            else
                print "You bend down, and";
            if (Apartment.sand_obj && Apartment.sand_obj.number > 2) {
                print " see little";
                if (Apartment.sand_obj.number < 5)
                    print " but a bit of sand";
                else
                    print " but sand";
                ".";
            }
            " see dust and assorted minor trash under the futon.
                Nothing unusual.";
        Pull, Push, Turn:
            if (OIU(self)) rtrue;
            "The futon is theoretically easy to move. In practice, you
                never bother.";
        Dig:
            if (OIU(self)) rtrue;
            "No loose change, sorry.";
    ],
    react_before [;
        Exit:
            if (player in self) {
                if (self.leave_barrier == NULL) {
                    return self.Zone::react_before();
                }
                self.leave_barrier = NULL;
                move player to Apartment;
                Goaled(firstgoal);
                "You lever yourself upright. Umf. It's amazing how much lack
                    of sleep feels like a hangover, only without the preceding
                    party.";
            }
    ],
  has supporter;

Zone desk "desk"
  with
    name 'desk',
    description [ num;
        print "The desk is, of course, an
            organized mess; overlapping piles of paper, inches high.
            The computer is a dusty beige block, several generations
            out of date, poking up hopefully from among the strata.
            The lamp hangs over all.^";
        if (computer has on) {
            print "^A game-over message is flashing on the screen.^";
        }
        num = Locale(self, "You see", "You also see");
        if (num) {
            print " on the desk.^";
        }
        rtrue;
    ],
    nolocifin true,
    leave_fragment " stand up",
    enter_fragment " sit down at the desk",
    locseealso kitchen bathroom,
    before [ count ix;
        LookUnder:
            if (OIU(self)) rtrue;
            if (Apartment.sand_obj && Apartment.sand_obj.number > 2) {
                print "Nothing under the desk";
                if (Apartment.sand_obj.number < 5)
                    print " but a bit of sand";
                else
                    print " but sand";
                ".";
            }
            "Nothing interesting is under the desk.";
        Take, Push, Pull:
            if (OIU(self)) rtrue;
            "The desk is too heavy to move.";
        Search:
            if (OIU(self)) rtrue;
            if (GoalOpen(self) || GoalOpen(tickets))
                <<Search papers>>;
            count = 0;
            objectloop (ix in self) {
                if (ix hasnt concealed && ix hasnt scenery)
                    count++;
            }
            print "Aside from the computer, the lamp, and piles of paper";
            if (count == 0)
                ", there is nothing on the desk.";
            print ", you see ";
            WriteListFrom(child(self),
                TERSE_BIT + ENGLISH_BIT + CONCEAL_BIT);
            ".";
    ],
    prereqs closet shower,
    opentime 0,
    imp [;
        if (~~GoalClosed(kitchen))
            return;
        if (~~GoalClosed(bathroom))
            return;
        self.opentime++;
        switch (self.opentime) {
            3: print "^You hear a car approaching outside.^";
            4: print "^A car horn honks outside.^";
            6: print "^The car honks again, more urgently. Your taxi --
                it must be.^";
            7: print "^The taxi honks repeatedly.^";
            9: print "^The taxi honks repeatedly. The sound
                seems a little fainter now.^";
            11: self.opentime = 6;
        }
    ],
  has supporter;

Object -> papers "papers"
  with
    name 'paper' 'papers' 'strata' 'pile' 'piles',
    description [;
        print "The papers encompass everything you've done, thought,
            or written in the past year. And none of it is really worth
            looking through.^";
        if (GoalOpen(tickets)) {
            if (self.ticket_search == 0) {
                self.ticket_search = 1;
                print "^Or -- hmm. Maybe you left the plane tickets on the
                    desk yesterday, before that late-night IF work session?^";
            }
        }
        rtrue;
    ],
    seenonce false,
    ticket_search 0,
    before [;
        Examine:
            if (OIU(self)) rtrue;
            if (GoalOpen(desk))
                <<Search self>>;
            self.description();
            rtrue;
        Search, LookUnder:
            if (OIU(self)) rtrue;
            if (GoalOpen(desk)) {
                plant.startup(2);
                RescueContents(desk);
                move computer to Apartment;
                give computer ~on;
                remove desk;
                Goaled(desk);
                "You carefully tilt up piles of paper, trying to avoid an
                    avalanche. Yes, sand is scattered beneath.^^Carefully?
                    You shove a paper-stack off the desk; it's a shower
                    of sand before it hits the ground. Ha! You push another,
                    and another, and then sweep the whole mass over the
                    edge. White sand cascades everywhere. Laughing, you
                    feel the desk itself give way.^^", (emph) "Pop.",
                    " Oops. That was the light bulb imploding. The apartment
                    is very dark and quiet of a sudden.";
            }
            if (GoalOpen(tickets)) {
                print "You carefully tilt up piles of paper, trying to
                    avoid an avalanche. ";
                CheckTicket(self);
                rtrue;
            }
            "Nothing in all that mess do you want to see again, really.";
        Take, Push, Pull:
            if (OIU(self)) rtrue;
            if (GoalOpen(desk) || GoalOpen(tickets))
                <<Search self>>;
            print "What? And disorganize your piles? You'd never find a thing
                again";
            if (~~self.seenonce) {
                self.seenonce = true;
                print "... well, not that you've looked for anything
                    in these piles ever before. Your life merely accumulates,
                    you've decided; it does not build";
            }
            ".";
    ],
  has scenery pluralname;

Object -> lamp "lamp"
  with
    name 'desk' 'lamp' 'clothespins' 'pin' 'pins',
    parse_name [ wd firstwd num;
        wd = NextWord();
        firstwd = wd;
        while (WordInProperty(wd, self, name)) {
            num++;
            wd = NextWord();
        }
        if (num == 1 && firstwd == 'desk')
            return 0;
        return num;
    ],
    description "The lamp is a peculiar goosenecked item that you found
        at a yard sale. It's clamped to the side of the desk, and balanced
        with a collection of clothespins. The lamp casts an uncomfortable
        yellow glare around the desk; it makes your eyes ache.",
    before [;
        Search:
            if (OIU(self)) rtrue;
            "You squint into the blazing light. If there is truth in
                there, you are blind to it.";
        Take, Pull, Push, Turn:
            if (OIU(self)) rtrue;
            "You don't dare fiddle with it. Only a precarious arrangement
                of clamps and clothespins prevents the lamp from
                giraffing itself flat and setting your desk on fire.";
        Touch:
            if (OIU(self)) rtrue;
            "The lamp is hot.";
        SwitchOn:
            if (OIU(self)) rtrue;
            "The lamp is already on.";
        SwitchOff:
            if (OIU(self)) rtrue;
            "You do not want the dark.";
    ],
  has scenery;

Object -> computer "computer"
  with
    name 'computer' 'dusty' 'beige' 'block' 'screen' 'keyboard' 'monitor'
        'text' 'game' 'adventure' 'if' 'interactive' 'fiction',
    seenonce false,
    description [;
        print "The computer is years old, and incompatible with just about
            everything but electricity. You use it for writing -- at least,
            you try to believe that you do.^";
        if (self has on) {
            if (~~self.seenonce) {
                self.seenonce = true;
                print "^Right now, however, there's a game on the screen --
                    one of the text adventures, or interactive fictions,
                    or whatever they are this month -- the only kind of game
                    your beige antique can run, anyway.^";
            }
            print "^The you-have-died message is blinking morosely at you.
                You started up ", (emph) "Ready, Okay!", " last night,
                trying to distract yourself until morning.
                But you can't get even halfway through without running
                out of insulin.^";
        }
        else {
            print "^The computer is off.^";
        }
        rtrue;
    ],
    nosubdescribe [; return (self in desk); ],
    subdescribe "The computer has survived somehow, and lies in a
        pile of sand.",
    before [;
        Examine, Search:
            if (OIU(self)) rtrue;
            self.description();
            rtrue;
        Play, TypeOn:
            if (OIU(self)) rtrue;
            if (self hasnt on)
                "The computer isn't on.";
            "You've tried everything you can think of twice, and you're
                tired of trying. Maybe an idea will come to you later.";
        SwitchOff:
            if (OIU(self)) rtrue;
            if (self hasnt on)
                "The computer is already off.";
            give self ~on;
            "You hit the power key; the computer gives a tiny sigh and
                shuts down.";
        SwitchOn:
            if (OIU(self)) rtrue;
            if (self has on)
                "The computer is already on.";
            "You hit the power key; nothing happens. Terrific. Something's
                finally fried itself in the bowels of your twelve-megahertz
                wonder, and you may never die in a text adventure
                again.";
        Attack:
            if (OIU(self)) rtrue;
            if (self has on)
                "Violence isn't the answer to that game.";
            else
                "You thump hopefully. But when you hit the power key again,
                    the computer remains dead.";
        Smell:
            if (OIU(self)) rtrue;
            if (self has on)
                "The computer smells of ozone and fried dust.";
            "You lean close. Yes, the scent of burnt insulation drifts
                from the vents.";
        Open:
            if (OIU(self)) rtrue;
            "This model can't be opened except at the factory. (And as far
                as you know, the factory was destroyed in the Pyong-Yang
                Flood of 1990.)";
        LookUnder:
            if (OIU(self)) rtrue;
            "Yes, your computer has LRF support.";
    ],
  has scenery on;

Object -> todo "to-do list"
  with
    name 'list' 'to-do' 'to' 'do' 'todo' 'task',
    articles "Your" "your" "your",
    description [;
        print "The task list for your impending journey is a mess of 
            crossed-out items and scribbled corrections.";
        if (GoalClosed(window)) {
            " It is entirely illegible.";
        }
        if (GoalOpen(window)) {
            print " At the very bottom of the page, you see:^";
            print "  - call taxi^";
            print "  - wait for taxi^";
            print "  - look, make sure it's actually taxi before
                opening door^";
            "  (That last entry in a hasty, nervous script.)";
        }
        if (GoalClosed(bathsink) || GoalClosed(kitchensink)) {
            print " In the middle of the page, you see:^";
            if (GoalOpen(closet))
                print "  - clean out closet^";
            if (GoalOpen(crate))
                print "  - buy stereo stand^";
            if (GoalOpen(stove))
                print "  - don't leave stove on^";
            if (GoalOpen(fridge))
                print "  - defrost refrigerator^";
            if (GoalOpen(shower))
                print "  - unclog shower drain^";
            if (GoalOpen(luggage))
                print "  - pack clean socks^";
            if (GoalOpen(cupboard))
                print "  - remove package from kitchen storage^";
            if (GoalOpen(desk))
                print "  - find notes for IFComp entry^";
            if (GoalOpen(toilet))
                print "  - flush notes down toilet^";
            if (GoalOpen(kitchen))
                print "  - stay out of kitchen!^";
            if (GoalOpen(bathroom))
                print "  - stay out of bathroom!^";
            rtrue;
        }
        if (GoalOpen(bathsink) || GoalOpen(kitchensink)) {
            " Dammit, yet another item you missed -- you have to water
                the plant.";
        }
        if (GoalOpen(vacuum)) {
            " Huh. ~Vacuum~ is checked; you remember doing it. But
                look at this floor.";
        }
        if (GoalOpen(tickets)) {
            " There, near the bottom; ~Buy plane tickets~, and a checkmark.
                So where did you leave them? The desk? Your coat pocket?
                Could they have gotten packed accidentally?";
        }
        " However, everything seems
            to have gotten done. The last line reads ~Call taxi~, and it
            has its checkmark, and here you wait.";
    ],
    before [;
        Examine, Search:
            if (OIU(self)) rtrue;
            self.description();
            rtrue;
    ],
  has rescuable;

Object -> book "travel book"
  with
    name 'travel' 'survival' 'book' 'schmendrick' 'desert' 'camping' 'guide',
    saw_blank false,
    number 0,
    description [;
        if (GoalClosed(mirror)) {
            if (player.orders == 0) {
                print (emph) "The Desert Elemental's Handbook",
                    " -- you've been studying it for ages. Trace moisture
                    segregation, arthropod ecocycles, sand/grit/fines
                    sizing distributions. And, of course, the artistic
                    aspects of heat, time, distance, and death.^";
                switch (random(91) % 9) {
                    0: print "^You're especially fond of";
                    1: print "^You re-read";
                    2: print "^You look up";
                    3: print "^You come across";
                    4: print "^You study";
                    5: print "^You memorize";
                    6: print "^You skim through";
                    7: print "^You're fond of";
                    8: print "^You consider";
                }
                switch (random(107) % 9) {
                    0: print " the chapters on";
                    1: print " a section on";
                    2: print " some tables of";
                    3: print " the pages on";
                    4: print " a few paragraphs about";
                    5: print " some key points about";
                    6: print " the chapter about";
                    7: print " a page of";
                    8: print " an essay on";
                }
                self.number++;
                switch (self.number) {
                    1: print " bones";
                    2: print " starvation";
                    3: print " scorpions";
                    4: print " thirst";
                    5: print " snakes";
                    6: print " sandstorms";
                    7: print " sunstroke";
                    8: print " stings";
                    9: print " thirst";
                    10: print " dunes";
                    11: print " aridity";
                    default: 
                        print " thirst";
                        self.number = 0;
                }
                ".";
            }
            else {
                self.saw_blank = true;
                "The cover is worn now. The pages are all blank.";
            }
        }
        if (GoalClosed(fridge) || GoalClosed(shower) || GoalClosed(luggage)) {
            print (emph) "The Schmendrick's Guide to the Desert",
                " -- you've been studying it for what seems like forever.
                Endless miles of information: colors of silence,
                sounds of heat and sunlight, the feel of vast distances
                on your skin, and death. It sounds beautiful.^";
            rtrue;
        }
        if (GoalClosed(vacuum)) {
            print (emph) "The Schmendrick's Guide to Desert Camping",
                " -- you've been studying it for weeks now. Vast alien
                reams of information: perspiration rates, sunburn
                factors, how to avoid dying of thirst, how to avoid
                dying of heat. It sounds challenging.^";
            BackgroundInfo(self, "But that's why you're going on
                this trip.");
            rtrue;
        }
        print (emph) "The Schmendrick's Guide to Desert Camping",
            " -- you've been studying it for weeks now. Vast alien
            reams of information: perspiration rates, sunburn
            factors, tent guying, and how to make a condensation still
            out of a trashbag and a tin can. You could probably
            write the book out longhand from memory; and none of it
            means a damn thing to you.^";
        BackgroundInfo(self, "That's why you're going on this trip,
            after all.");
        rtrue;
    ],
    background_flag false,
    before [;
        Examine, Search, Open:
            if (OIU(self)) rtrue;
            self.description();
            rtrue;
    ],
  has rescuable;
! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


Class FloorSand
  with
    name 'sand' 'sand', ! will be adjusted later
    short_name [; 
        print "sand on the ", (string) self.self_word, " floor";
        !print " ['", (address) (self.&name)-->0, 
        !    "' '", (address) (self.&name)-->1, "']";
        rtrue;
    ],
    parse_name [ wd gotsand num lastwd alt1 alt2 addr;
        addr = self.&name;
        alt1 = addr-->0;
        alt2 = addr-->1;
        wd = NextWord();
        while (WordInProperty(wd, self.self_name, name) 
            || wd == 'sand' or 'on' or 'in' or 'of' or 'floor' 
                or alt1 or alt2) {
            if (wd == 'sand' or alt1 or alt2)
                gotsand = true;
            lastwd = wd;
            num++;
            wd = NextWord();
        }
        if (~~gotsand)
            return 0;
        if (num > 0 && lastwd == 'on' or 'in')
            return num-1;
        return num;
    ],
    article "some",
    number 0,
    partinvent [;
        switch (self.number) {
            0: print "[BUG] no sand";
            1: print "a trace of sand";
            2: print "a trail of sand";
            3: print "some sand";
            4: print "a thin layer of sand";
            5: print "a heavy layer of sand";
            6: print "an inch of sand";
            7: print "inches of sand";
            8: print "ankle-deep sand";
            default: print "hills of sand";
        }
        rtrue;
    ],
    description [;
        switch (self.number) {
            0: "[BUG] No sand present";
            1: "A trace of sand is visible on the ",
                    (string) self.self_word, " floor.";
            2: "A trail of sand is visible on the ",
                    (string) self.self_word, " floor.";
            3: "Sand is spilled across the ",
                    (string) self.self_word, " floor.";
            4: "There is a thin layer of sand on the ",
                    (string) self.self_word, " floor.";
            5: "There is a heavy layer of sand on the ",
                    (string) self.self_word, " floor.";
            6: "Sand lies an inch deep across the ",
                    (string) self.self_word, " floor.";
            7: "Sand lies inches deep across the ",
                    (string) self.self_word, " floor.";
            8: "The sand on the ",
                    (string) self.self_word, " floor
                    is ankle-deep.";
            default: "Sand fills the ",
                    (string) self.self_word, ".";
        }
    ],
    before [;
        Take:
            if (OIU(self)) rtrue;
            switch (self.number) {
                0, 1, 2:
                    print "You scrape up a pinch of sand";
                default:
                    print "You pick up a pinch of sand";
            }
            ". It runs through your fingers; it's sand.";
        BlowOn:
            if (OIU(self)) rtrue;
            "A few grains of sand tumble away.";
        Push, Pull:
            if (OIU(self)) rtrue;
            print "You push the sand around with your foot.";
            if (self.number <= 4)
                print " It scrapes uncomfortably against the floor.";
            new_line;
            rtrue;
        Insert:
            if (OIU(self)) rtrue;
            if (second ~= glass)
                "That won't hold sand.";
            if (glass notin player)
                "You're not holding the glass.";
            if (sand in glass)
                "The glass is already full.";
            move sand to glass;
            PronounNotice(sand);
            if (self.number <= 2)
                print "You scrape together enough sand to fill the glass.";
            else
                print "You scoop up a glassful of sand.";
            if (water in glass) {
                remove water;
                print " The water in the glass turns cloudy as the sand
                    falls in; then it seems to retreat, until the sand
                    is as dry as that at your feet. Strange.";
            }
            new_line;
            rtrue;
        Drink, Taste:
            if (OIU(self)) rtrue;
            "You cannot drink sand.";
        Dig:
            if (OIU(self)) rtrue;
            switch (self.number) {
                0 to 3: 
                    "There's hardly enough sand there to dig in.";
                4 to 6:
                    "You scrape the sand away until the floor shows.
                        It wasn't an even layer to begin with, of course.";
                7, 8:
                    "You scrape the sand away until a bit of floor shows.
                        But the sand pours back down as soon as you stop.";
                default:
                    "You cannot seem to find the floor.";
            }
        Find:
            "It's hard to miss.";
    ],
    receive_sand [; <<Drop sand>>; ],
    receive_water [; <<Drop water>>; ],
  has static;

[ BumpSand obj newval addr;
    if (~~(obj ofclass FloorSand)) {
        switch (obj) {
            kitchen: obj = kitchensand;
            bathroom: obj = bathroomsand;
            shower: obj = showersand;
            Apartment: obj = livingsand;
            default:
                print_ret "[BUG] BumpSand(", (name) obj, ")";
        }
    }
    if (obj notin obj.self_obj) {
        move obj to obj.self_obj;
        obj.self_obj.sand_obj = obj;
    }
    if (newval == 0) {
        obj.number++;
    }
    else {
        if (obj.number < newval)
            obj.number = newval;
    }
    
    addr = obj.&name;
    addr-->1 = 'sand';
    switch (obj.number) {
        0: addr-->0 = 'buggy';
        1: addr-->0 = 'trace';
        2: addr-->0 = 'trail';
        3: addr-->0 = 'some';
        4: addr-->0 = 'thin'; addr-->1 = 'layer';
        5: addr-->0 = 'heavy'; addr-->1 = 'layer';
        6: addr-->0 = 'inch';
        7: addr-->0 = 'inches';
        8: addr-->0 = 'ankle'; addr-->1 = 'deep';
        default: addr-->0 = 'hills'; addr-->1 = 'hill';
    }
];

FloorSand kitchensand
  with
    self_obj kitchen,
    self_name kitchen,
    self_word "kitchen";

FloorSand bathroomsand
  with
    self_obj bathroom,
    self_name bathroom,
    self_word "bathroom";

FloorSand livingsand
  with
    self_obj Apartment,
    self_name apartmentproxy,
    self_word "living room";

FloorSand showersand
  with
    self_obj shower,
    self_name shower,
    self_word "shower";

Class SandQuantity
  with
    description "The sand is white and pure.",
    before [;
        Take:
            if (OIU(self)) rtrue;
            print_ret "You take a pinch of sand from the ", 
                (string) self.self_name, ", and let it trickle back.";
        BlowOn:
            if (OIU(self)) rtrue;
            "A few grains of sand tumble away.";
        Drink, Taste, Eat:
            if (OIU(self)) rtrue;
            "You cannot drink sand.";
        Drop:
            if (~~IndirectlyContains(player, self))
                "You haven't got that.";
            "A bit of sand sifts out, but most of it seems to be solidly
                packed in.";
        Dig:
            if (OIU(self)) rtrue;
            "As far as you can tell, it's sand all the way down.";
    ],
  has scenery;

SandQuantity jarsand "jarful of sand"
  with
    name 'jarful' 'of' 'sand',
    self_name "jar",
    before [;
        Empty, EmptyT:
            <<Empty pbutter>>;
    ];

SandQuantity boxsand "boxful of sand"
  with
    name 'boxful' 'of' 'sand',
    self_name "box",
    before [;
        Empty, EmptyT:
            <<Empty crackers>>;
    ];

Class SinkSand
  class SandQuantity,
  with
    short_name [;
        print "sand in the ", (string) self.self_name;
        rtrue;
    ],
    parse_name [ wd gotsand num lastwd;
        wd = NextWord();
        while (wd == self.self_word or 'sink'
            || wd == 'sand' or 'on' or 'in') {
            if (wd == 'sand')
                gotsand = true;
            lastwd = wd;
            num++;
            wd = NextWord();
        }
        if (~~gotsand)
            return 0;
        if (num > 0 && lastwd == 'on' or 'in')
            return num-1;
        return num;
    ],
    article "some",
    before [;
        Insert:
            if (OIU(self)) rtrue;
            if (second ~= glass)
                "That won't hold sand.";
            if (glass notin player)
                "You're not holding the glass.";
            if (sand in glass)
                "The glass is already full.";
            move sand to glass;
            PronounNotice(sand);
            print "You scoop up a glassful of sand.";
            if (water in glass) {
                remove water;
                print " The water in the glass turns cloudy as the sand
                    falls in; then it seems to retreat, until the sand
                    is as dry as that in the sink. Strange.";
            }
            new_line;
            rtrue;
        EmptyT:
            <<Insert self second>>;
    ],
    receive_water "You pour the water into the sink, where it soaks into
        the sand.",
    receive_sand "You add your sand to the sink's burden.";

SinkSand kitchensinksand
  with
    self_word 'kitchen',
    self_name "kitchen sink";

SinkSand bathsinksand
  with
    self_word 'bathroom',
    self_name "bathroom sink";

SandQuantity sand "glassful of sand"
  with
    name 'glassful' 'of' 'sand',
    self_name "glass",
    before [ ix jx;
        Drop:
            if (~~IndirectlyContains(player, self))
                "You haven't got that.";
            remove self;
            ix = FindFloorZone(player);
            if (ix)
                jx = ix.sand_obj;
            if (jx && jx.number >= 3)
                "The sand pours to the ground and is lost.";
            if (jx) {
                if (jx.number < 2)
                    BumpSand(jx);
            }
            else {
                if (ix)
                    BumpSand(ix);
            }
            "The sand scatters across the floor.";
        EmptyT:
            <<Insert self second>>;
        Insert, PutOn:
            if (~~IndirectlyContains(player, self))
                "You haven't got that.";
            if (second provides receive_sand) {
                if (OIU(second)) rtrue;
                ix = second.receive_sand();
                if (ix) {
                    if (ix ~= 2)
                        remove self;
                    rtrue;
                }
            }
            if (second == d_obj)
                <<Drop self>>;
            "Sand will do that no good.";
    ];

! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.


Class Zone
  with
    leave_fragment [;
        print " step out of ", (the) self;
        rtrue;
    ],
    enter_fragment [;
        print " step into ", (the) self;
    ],
    react_before [;
        Exit:
            if (player in self) {
                if (self provides leave_barrier 
                    && self.leave_barrier ~= NULL) {
                    PrintOrRun(self, leave_barrier);
                    rtrue;
                }
                move player to parent(self);
                if (keep_silent == 0) {
                    print "You";
                    PrintOrRun(self, leave_fragment, 1);
                    print ".^";
                }
                rtrue;
            }
    ],
    before [;
        Enter:
            if (player in self)
                return L__M(##Enter, 1, self);
            MoveToZone(self, 1);
            rtrue;
        Receive:
            if (self has supporter)
                rfalse;
            if (player notin self)
                MoveToZone(self);
            <<Drop noun>>;
    ],
    sand_obj 0,
  has scenery enterable transparent;

[ FindZone obj;
    obj = parent(obj);
    while (obj) {
        if (obj ofclass Zone || obj == Apartment)
            return obj;
        obj = parent(obj);
    }
    return 0;
];

[ FindFloorZone obj res;
    res = FindZone(obj);
    if (res && res has supporter)
        res = parent(res);
    return res;
];

[ MoveToZone zo skipfinal    anc ix jx count altform;
    count = 0;
    
    altform = multiflag;
    if (skipfinal)
        altform = false;
    
    anc = CommonAncestor(zo, player);
    if (anc == 0)
        "You cannot find your way.";
    ix = parent(player);
    while (ix ~= anc) {
        if (ix provides leave_barrier && ix.leave_barrier ~= NULL) {
            if (count > 0) {
                if (~~altform)
                    print ".^^";
                else
                    print ".) ";
            }
            PrintOrRun(ix, leave_barrier);
            rtrue;
        }
        if (count == 0) {
            if (altform)
                print "(";
            print "You";
        }
        else {
            print ", and";
        }
        PrintOrRun(ix, leave_fragment, 1);
        count++;
        ix = parent(ix);
        if (ix == 0) 
            "[BUG: Zoning: ix == 0]";
    }
    while (ix ~= zo) {
        jx = zo;
        while (jx notin ix) {
            jx = parent(jx);
            if (jx == 0) 
                "[BUG: Zoning: jx == 0]";
        }
        ix = jx;
        if (count == 0) {
            if (altform)
                print "(";
            print "You";
        }
        else {
            print ", and";
        }
        PrintOrRun(ix, enter_fragment, 1);
        count++;
    }
    if (count > 0) {
        if (~~altform) {
            print ".^";
            if (~~skipfinal)
                print "^";
        }
        else {
            print ".)";
            if (~~skipfinal)
                print " ";
        }
    }
    if (player notin zo)
        move player to zo;
    rfalse;
];

Constant OIU = ObjectIsUntouchable;

[ ObjectIsUntouchable item flag1 flag2   zo inokay ix;

    inokay = FALSE;
    if (item ofclass Zone) {
        zo = parent(item);
        inokay = TRUE;
    }
    else {
        zo = parent(item);
        while (~~(zo == 0 || zo ofclass Zone || zo == Apartment)) {
            zo = parent(zo);
        }
    }
    if (zo) {
        ! print "[", (name) item, ": ", (name) zo, " zone.]^";
        if (inokay && IndirectlyContains(item, player)) {
            zo = item;
        }
        ix = MoveToZone(zo);
        if (ix)
            return ix;
    }
    
    return Orig_OIU(item, flag1, flag2);
];

[ Orig_OIU item flag1 flag2 ancestor i;

  ! Determine if there's any barrier preventing the player from moving
  ! things to "item".  Return false if no barrier; otherwise print a
  ! suitable message and return true.
  ! If flag1 is set, do not print any message.
  ! If flag2 is set, also apply Take/Remove restrictions.

  ! If the item has been added to scope by something, it's first necessary
  ! for that something to be touchable.

  i = ObjectScopedBySomething(item);
  if (i ~= 0)
  {   if (ObjectIsUntouchable(i)) return;
      ! An item immediately added to scope
  }

  ancestor = CommonAncestor(player, item);

  ! First, a barrier between the player and the ancestor.  The player
  ! can only be in a sequence of enterable objects, and only closed
  ! containers form a barrier.

  if (player ~= ancestor)
  {   i = parent(player);
      while (i~=ancestor)
      {   if (i has container && i hasnt open)
          {   if (flag1) rtrue;
              return L__M(##Take,9,i);
          }
          i = parent(i);
      }
  }

  ! Second, a barrier between the item and the ancestor.  The item can
  ! be carried by someone, part of a piece of machinery, in or on top
  ! of something and so on.

  if (item ~= ancestor)
  {   i = parent(item);
      while (i~=ancestor)
      {   if (flag2 && i hasnt container && i hasnt supporter)
          {   if (i has animate)
              {   if (flag1) rtrue;
                  return L__M(##Take,6,i);
              }
              if (i has transparent)
              {   if (flag1) rtrue;
                  return L__M(##Take,7,i);
              }
              if (flag1) rtrue;
              return L__M(##Take,8,item);
          }
          if (i has container && i hasnt open)
          {   if (flag1) rtrue;
              return L__M(##Take,9,i);
          }
          i = parent(i);
      }
  }
  rfalse;
];

[ RescueContents loc dest   ix ix2;
    if (dest == 0)
        dest = Apartment;
    if (loc == 0 || loc == dest)
        print_ret "[BUG] RescueContents()";
    
    ix = child(loc);
    while (ix) {
        ix2 = sibling(ix);
        
        if (ix has rescuable) {
            move ix to dest;
        }
        else {
            if (child(ix))
                RescueContents(ix, dest);
        }
        
        ix = ix2;
    }
];
! _Shade_
! Special source-code edition
! Copyright 2000-2002 Andrew Plotkin <erkyrath@eblong.com>
! http://www.eblong.com/zarf/if.html
!   This source code is provided for personal, educational use only. 
!   See README file for details.

Constant Story "SHADE";
Constant Headline "^A brief story by ~Ampe R. Sand~ (Andrew Plotkin)^
    First-time players should type ~about~.^";
Release 3;

!Constant ZDEBUG;

Constant WORDSIZE 2;
Constant MANUAL_PRONOUNS;
Constant NO_PLACES;
Constant SKIP_MAGIC_ARTICLES;
Constant DIALECT_US;
!Constant AMERICAN_COMMAS;
Constant USE_PARTINVENT;

Attribute goal_complete;
Attribute rescuable;
Property prereqs;

Replace Banner;
Replace Keyboard;
Replace DrawStatusLine;
Replace ObjectIsUntouchable;
Replace OffersLight;
Replace SayWhatsOn;
Replace ScoreSub; 
Replace FullScoreSub; 
Replace SorrySub; 
Replace StrongSub; 
Replace MildSub; 
Replace PraySub;
Replace LMode3Sub;
Replace LookSub;
Replace SingSub;

Include "Parser";

#ifndef DEBUG;
Global debug_flag;
#endif;

Object LibraryMessages "lib_messages"
  with
    seenonce, startup, shutdown, opentime,
    subdescribe, nosubdescribe, nosupportlist, locseealso, nolocifin,
    self_word, self_name, self_obj, sand_obj,
    leave_barrier, leave_fragment, enter_fragment,
    receive_water, receive_sand, background_flag, ticket_search, 
    weakness, saw_blank,
    before [ ix;
        Miscellany:
            switch (lm_n) {
                4: print " SHADE "; rtrue;
            }
        Examine:
            switch (lm_n) {
                3: rtrue; ! squash "switchable is on/off" message
            }
        Take:
            switch (lm_n) {
                7:    if (lm_o ofclass Zone) {
                        if (noun has pluralname) print "Those are";
                        else print "That is";
                        if (lm_o has supporter) print " on";
                        else print " in";
                        print (the) lm_o;
                        ".";
                    }
            }
        Insert:
            switch (lm_n) {
                2:    if (lm_o == toilet) {
                        "Flushing your life down the toilet is just a figure
                            of speech, okay?";
                    }
            }
        Drop:
            switch (lm_n) {
                4:    print "Dropped";
                    ix = parent(player);
                    if (ix ofclass Zone) {
                        if (ix has supporter)
                            print " (onto ", (the) ix, ")";
                        else
                            print " (in ", (the) ix, ")";
                    }
                    ".";
            }
        Look:
            switch (lm_n) {
                1:
                    if (lm_o == desk)
                        print ", at ";
                    else
                        print ", on ";
                    print (the) lm_o; 
                    rtrue;
                2: print ", in ", (the) lm_o; rtrue;
                 5, 6: 
                     if (lm_o provides nolocifin && lm_o.nolocifin()
                         && IndirectlyContains(lm_o, player))
                         rtrue;
                     ! print "[ Locale ", (name) lm_o, " ]";
                     if (lm_o~=location) {
                         if (lm_o has supporter) print "^On "; 
                         else print "^In ";
                        print (the) lm_o, " you";
                    }
                    else {
                        print "^You";
                    }
                    print " can ";
                    if (lm_n==5) print "also "; 
                    print "see ";
                    WriteListFrom(child(lm_o),
                        ENGLISH_BIT + WORKFLAG_BIT + RECURSE_BIT
                        + PARTINV_BIT + TERSE_BIT + CONCEAL_BIT);
                    if (lm_o ~= location) 
                        ".";
                    if (lm_o ~= parent(player) && lm_o == Apartment) {
                        if (parent(player) has supporter)
                            " lying on the floor.";
                        else
                            " lying in the living room.";
                    }
                    " here.";
            }
        Fill:
            "That makes no sense.";
        Sleep:
            if (player.orders)
                "If only.";
            print "You've been trying all night. It's not going to happen";
            if (player in futon)
                print ", no matter how long you lie here";
            ".";
        Wake:
            "If only.";
        Drink:
            "Unlikely.";
        Dig:
            "You can't dig in that.";
        LMode1: " is now in its ~brief~ printing mode, which gives
                    long descriptions of places never before visited and short
                    descriptions otherwise.";
        LMode2: " is now in its normal ~verbose~ mode, which always gives long
                    descriptions of locations
                    (even if you've been there before).";
    ];

Include "VerbLib";

! --- Classes and other initial things

#include "shade-zone.inf";
#include "shade-sand.inf";
#include "shade-goal.inf";
#include "shade-room.inf";
#include "shade-nook.inf";
#include "shade-fake.inf";
#include "shade-dese.inf";

! --- Game startup

[ Initialise ix;
    ix = debug_flag;
    goalhandler.initial();
    
    move futon to Apartment;
    move desk to Apartment;
    move kitchen to Apartment;
    move bathroom to Apartment;
    move glass to counter;
    
    player = myselfobj;
    location = futon;
    
    lookmode = 2; ! verbose
    inventory_style = FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT; ! wide
    
    #ifdef DEBUG;
        print "[BUG] DEBUG on!^^";
    #endif;
    #ifdef ZDEBUG;
        move wand to player;
        print "[BUG] ZDEBUG on!^^";
    #endif;
];

Object myselfobj "(self object)"
  with 
    short_name "yourself",
    description "You're you.",
    capacity 100,
    number 0,
    orders 0,
    before [;
        Empty:
            "You know you are already empty.";
    ],
    receive_water [;
        print "You want to dump the water on your head?
            Potentially enjoyable; actually messy.^";
        return 2;
    ],
    receive_sand [;
        print "You're not a chinchilla.^";
        return 2;
    ],
    weakness 0,
  has concealed animate proper transparent rescuable;

#ifdef ZDEBUG;
Object wand "magic wand"
  with
    name 'magic' 'wand',
    before [;
    ];
#endif;

! --- Grammar and suchlike helpers

[ Keyboard  a_buffer a_table  nw i w w2 x1 x2;

    DisplayStatus();
    .FreshInput;

!  Save the start of the buffer, in case "oops" needs to restore it
!  to the previous time's buffer

    for (i=0:i<64:i++) oops_workspace->i = a_buffer->i;

!  In case of an array entry corruption that shouldn't happen, but would be
!  disastrous if it did:

   a_buffer->0 = 120;
   a_table->0 = 15;  ! Allow to split input into this many words

!  Print the prompt, and read in the words and dictionary addresses

    L__M(##Prompt);
    AfterPrompt();
    #IFV5; DrawStatusLine(); #ENDIF;
    KeyboardPrimitive(a_buffer, a_table);
    nw=a_table->1;

!  If the line was blank, get a fresh line
    if (nw == 0)
    { L__M(##Miscellany,10); jump FreshInput; }

!  Unless the opening word was "oops", return

    w=a_table-->1;
    if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops;

#IFV5;
!  Undo handling

    if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (parse->1==1))
    {   if (turns==1)
        {   L__M(##Miscellany,11); jump FreshInput;
        }
        if (undo_flag==0)
        {   L__M(##Miscellany,6); jump FreshInput;
        }
        if (undo_flag==1) jump UndoFailed;
        if (just_undone==1)
        {   L__M(##Miscellany,12); jump FreshInput;
        }
        @restore_undo i;
        if (i==0)
        {   .UndoFailed;
            L__M(##Miscellany,7);
        }
        jump FreshInput;
    }
    @save_undo i;
    just_undone=0;
    undo_flag=2;
    if (i==-1) undo_flag=0;
    if (i==0) undo_flag=1;
    if (i==2)
    {   !style bold;
        !print (name) location, "^";
        !style roman;
        L__M(##Miscellany,13);
        just_undone=1;
        jump FreshInput;
    }
#ENDIF;

    return nw;

    .DoOops;
    if (oops_from == 0)
    {   L__M(##Miscellany,14); jump FreshInput; }
    if (nw == 1)
    {   L__M(##Miscellany,15); jump FreshInput; }
    if (nw > 2)
    {   L__M(##Miscellany,16); jump FreshInput; }

!  So now we know: there was a previous mistake, and the player has
!  attempted to correct a single word of it.

    for (i=0:i<=120:i++) buffer2->i = a_buffer->i;
    x1 = a_table->9; ! Start of word following "oops"
    x2 = a_table->8; ! Length of word following "oops"

!  Repair the buffer to the text that was in it before the "oops"
!  was typed:

    for (i=0:i<64:i++) a_buffer->i = oops_workspace->i;
    Tokenise__(a_buffer,a_table);

!  Work out the position in the buffer of the word to be corrected:

    w = a_table->(4*oops_from + 1); ! Start of word to go
    w2 = a_table->(4*oops_from);    ! Length of word to go

!  Write spaces over the word to be corrected:

    for (i=0:i<w2:i++) a_buffer->(i+w) = ' ';

    if (w2 < x2)
    {   ! If the replacement is longer than the original, move up...

        for (i=120:i>=w+x2:i--)
            a_buffer->i = a_buffer->(i-x2+w2);

        ! ...increasing buffer size accordingly.

        a_buffer->1 = (a_buffer->1) + (x2-w2);
    }

!  Write the correction in:

    for (i=0:i<x2:i++) a_buffer->(i+w) = buffer2->(i+x1);

    Tokenise__(a_buffer,a_table);
    nw=a_table->1;

    return nw;
];

Global parse_err_count = 0;

[ ParserError etype;
    switch (etype) {
        UPTO_PE:
            if (action_to_be == ##Vacuum) {
                print "Just ~", (address) verb_word, "~.";
                parse_err_count++;
                if (parse_err_count == 3) 
                    print " And the parser apologizes for not being smarter
                        about this.";
                else
                    print " When you clean, it's pretty much cleaning the
                        whole area.";
                new_line;
                rtrue;
            }
            rfalse;
    }
    rfalse;
];

Array roomnamebuffer -> 128;
[ DrawStatusLine width len pos ix;
    @output_stream 3 roomnamebuffer;
    if (deadflag)
        print "...";
    else if (location == thedark) 
        print (name) location;
    else {
        FindVisibilityLevels();
        if (visibility_ceiling == location)
            print (The) location;
        else print (The) visibility_ceiling;
        ix = parent(player);
        if (visibility_ceiling ~= ix) {
            if (ix has supporter) L__M(##Look, 1, ix);
            else L__M(##Look, 2, ix);
        }
        else {
            if (~~GoalClosed(window))
                print ", in the living room";
        }
    }
    @output_stream -3;
    len = (roomnamebuffer-->0);

    @split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
    width = 0->33;
    spaces width;

    pos = (width - len) / 2 + 1;
    @set_cursor 1 pos;
    for (ix=0 : ix<len : ix++)
        print (char) roomnamebuffer->(2+ix);
    @set_cursor 1 1; style roman; @set_window 0;
];

[ emph str;
    style underline;
    print (string) str;
    style roman;
];

[ KeyPause jx;
    print "[Hit any key...] ";
    @read_char 1 jx;
    new_line;
];

! Simple light function which says everything is lit.
[ OffersLight i;
    if (i == 0)
        rfalse;
    rtrue;
];

[ SayWhatsOn descon j f;
  if (descon==parent(player)) 
      rfalse;
  if (descon provides nosupportlist && descon.nosupportlist())
      rfalse;
  objectloop (j in descon)
      if (j hasnt concealed && j hasnt scenery) f=1;
  if (f==0) rfalse;
  L__M(##Look, 4, descon); rtrue;
];

[ DescribeParagraph loc started skipfinal ix;
    objectloop (ix in loc) {
        if (ix provides subdescribe && ix.subdescribe ~= NULL) {
            if (ix provides nosubdescribe && ix.nosubdescribe()) {
                ! nothing
            }
            else {
                if (~~started) {
                    print "^";
                    started = true;
                }
                else {
                    print " ";
                }
                PrintOrRun(ix, subdescribe, 1);
            }
        }
    }
    if (~~started)
        rfalse;
    if (~~skipfinal)
        print "^";
    rtrue;
];

[ ChooseObjects obj fl;
    if (fl == 0 or 1) {
        ! Scenery objects never count in "all".
        if (obj has scenery)
            return 2; 
        rfalse;
    }
    ! In general scoring, deprecate scenery, and particularly the walls.
    ! Tamed water gets a boost. Wild water in a different zone gets a
    ! penalty.
    if (obj in compass) {
        return 1;
    }
    if (obj ofclass SinkWater || obj ofclass Sink) {
        if (action_to_be == ##Find) {
            if (obj.self_word == 'bathroom') return 2;
            else return 1;
        }
        if (FindZone(player) ~= FindZone(obj))
            return 1;
    }
    if (obj == showerhead or showerheadwater) {
        if (action_to_be == ##Find) 
            return 1;
        if (FindZone(player) ~= FindZone(obj))
            return 1;
    }
    if (obj ofclass FloorSand || obj ofclass SinkSand) {
        if (FindZone(player) ~= FindZone(obj))
            return 1;
        else
            return 2;
    }
    
    if (action_to_be == ##Enter && verb_word == 'sit') {
        if (obj == futon or fakefuton or desfuton) {
            return 4;
        }
    }
    
    if (obj ofclass Fake) {
        return 2;
    }
    
    if (obj == water || obj ofclass SandQuantity) {
        return 3;
    }
    if (obj has scenery || obj == player) {
        return 2;
    }
    return 3;
];

Include "Grammar";

[ Banner i;
   new_line;
   print "    ";
#IFV5; style bold; #ENDIF;
   print "*** SHADE ***";
#IFV5; style roman; #ENDIF;
   if (Headline ~= 0)
       print (string) Headline;
   print "Release ", (0-->1) & $03ff, " / Serial number ";
   for (i=18:i<24:i++) print (char) 0->i;
   print " / Inform v"; inversion;
   print " Library ", (string) LibRelease, " ";
#ifdef STRICT_MODE;
   print "S";
#endif;
#ifdef INFIX;
   print "X";
#ifnot;
#ifdef DEBUG;
   print "D";
#endif;
#endif;
   new_line;
];

[ AboutSub;
    print "This is a one-room game set in your apartment.
        ...But perhaps you've already gathered that.^^";
    print "SHADE mostly uses the standard vocabulary of interactive
        fiction. You may find the command ~find...~ (or ~where is...~)
        convenient, though not critically important.^^";
    print "SHADE is copyright 2000 by Andrew Plotkin
        (erkyrath@@64eblong.com).
        He began coding it on the morning of September 2, 2000,
        and raced like
        the wind to finish it in time for the Sixth Annual IF Competition. 
        (SHADE placed tenth, which is pretty darn good, considering the
        overall excellence of the entries that year.)
        For more IF by the same author, 
        see^^    <http://www.eblong.com/zarf/if.html>^^
        SHADE may be distributed for free,
        but not sold or included in any for-profit collection without
        written permission from the author.^^";
    print "All hail the beta-tester: Michael Kinyon (still inevitable
        after all these years).
        And the people who sent bug reports after the competition,
        who include (but are not limited to):
        Ross Presser, Gunther Schmidl, Matthew Murray, Joe Mason,
        Nick Wolfe, Caleb Wilson, Neil Cerutti, Jason McIntosh,
        Daryl McCullough, Lelah Conrad, Jeff Hall.
        And -- this game is dedicated to Meatball Fulton.^";
    rtrue;
];

[ ScoreSub;
    if (deadflag)
        return;
    L__M(##Miscellany, 38);
];

[ FullScoreSub;
    ScoreSub();
];

[ LookSub allow_abbrev  visibility_levels i j k;
  if (parent(player)==0) return RunTimeError(10);

  .MovedByInitial;
  if (location == thedark) { visibility_ceiling = thedark; NoteArrival(); }
  else
  {   visibility_levels = FindVisibilityLevels();
      if (visibility_ceiling == location)
      {   NoteArrival();
          if (visibility_ceiling ~= location) jump MovedByInitial;
      }
  }

  ! The boldface intro line is kaput...
  !   The room description (if visible)

  if (lookmode<3 && visibility_ceiling==location)
  {   if ((allow_abbrev~=1) || (lookmode==2) || (location hasnt visited))
      {   if (location.describe~=NULL) RunRoutines(location,describe);
          else
          {   if (location.description==0) RunTimeError(11,location);
              else PrintOrRun(location,description);
          }
      }
  }

  if (visibility_levels == 0) Locale(thedark);
  else
  {   for (i=player, j=visibility_levels: j>0: j--, i=parent(i))
          give i workflag;
      
      for (j=visibility_levels: j>0: j--)
      {   for (i=player, k=0: k<j: k++) i=parent(i);
          if (i.inside_description~=0)
          {   new_line; PrintOrRun(i,inside_description); }
          Locale(i);
      }
      
      i = parent(player);
      if (i provides locseealso) {
          k = (i.#locseealso) / WORDSIZE;
          i = (i.&locseealso);
          for (j=0 : j<k : j++) {
              if (parent(i-->j))
                  Locale(i-->j);
          }
      }
  }

  LookRoutine();
  ScoreArrival();

  action=##Look;
  if (AfterRoutines()==1) rtrue;
];

[ FindSub zo ix jx;
    if (player == noun)
        "You know where you are.";
    if (IndirectlyContains(player, noun))
        "You've got ", (the) noun, ".";
    zo = FindZone(noun);
    if (~~zo)
        "Strange question.";
    print (The) noun, " ", (isorare) noun;
    if (zo has supporter) 
        print " on ";
    else
        print " in ";
    if (zo == Apartment)
        print "the living room";
    else
        print (the) zo;
    if (parent(noun) ~= zo) {
        jx = 0;
        for (ix = parent(noun) : ix && ix ~= zo : ix = parent(ix)) {
            if (ix has container || ix has supporter)
                jx = ix;
        }
        if (jx) {
            if (jx has supporter)
                print ", on ";
            else
                print ", in ";
            print (the) jx;
        }
    }
    ".";
];

[ FindMeSub; <<Find player>>; ];

[ RaiseSub;
    if (OIU(noun)) rtrue;
    if (noun has static || noun has scenery)
        "You are unable to.";
    "This is uninteresting.";
];

[ LowerSub;
    if (OIU(noun)) rtrue;
    if (noun has static || noun has scenery)
        "You are unable to.";
    "This is uninteresting.";
];

[ VacuumSub;
    if (~~TestScope(vacuum))
        "No cleaning implement is in sight.";
    <<SwitchOn vacuum>>;
];

[ WaterActionSub;
    if (~~IndirectlyContains(player, water))
        "You're not carrying any water.";
    <<PutOn water noun>>;
];

[ PlaySub;
    print_ret (cthatorthose) noun, " ", (isorare) noun, " no game.";
];

[ BlowOnSub;
    if (OIU(noun)) rtrue;
    print_ret "Blowing on ", (thatorthose) noun, " has no effect.";
];

[ TypeVagueSub obj;
    obj = 0;
    if (TestScope(computer))
        obj = computer;
    if (obj==0 && TestScope(fakecomputer))
        obj = fakecomputer;
    if (obj==0 && TestScope(descomputer))
        obj = descomputer;
    if (obj==0)
        "I'm not sure what you want to type on.";
    print "(on ", (the) obj, ")^";
    <<TypeOn obj>>;
];

[ TypeOnSub;
    print_ret "You cannot type on ", (thatorthose) noun, ".";
];

[ SingSub;
    if (GoalClosed(mirror))
        "There is no place for that here.";
    "This is no place for that.";
];

[ LMode3Sub;
    print_ret "~Superbrief~ mode is not available in ", (string) Story, ".";
];

[ SorrySub;
    L__M(##Miscellany, 38);
];

[ StrongSub;
    L__M(##Miscellany, 38);
];

[ MildSub;
    L__M(##Miscellany, 38);
];

[ PraySub;
    L__M(##Miscellany, 38);
];

#ifdef ZDEBUG;
[ ZapSub;
    if (player in FakeApartment) {
        Desert.startup();
        PlayerTo(Desert);
    }
    else if (player in Desert) {
        player.weakness = 0;
        player.orders = EndSuffering;
        StopDaemon(figure);
        StopDaemon(desstereo);
        "[Endgame]";
    }
    else
        PlayerTo(FakeApartment);
];
[ ZapRescueSub;
    print "Rescuing (and killing) ", (name) noun, ".^";
    RescueContents(noun);
    remove noun;
];
[ ZapSandSub obj;
    objectloop (obj ofclass FloorSand) {
        BumpSand(obj);
        print "Bumped ", (name) obj, ".^";
    }
];
#endif;

Verb meta 'about' 'info' 'help' 'hint'
    * -> About;

#ifdef ZDEBUG;
Verb 'zap'
    * -> Zap
    * 'sand' -> ZapSand
    * 'rescue' noun -> ZapRescue;
Verb 'goals'
    * -> GoalList
    * number -> GoalList;
Verb 'opengoal'
    * scope=GoalsScope -> ZapOpenGoal;
Verb 'closegoal'
    * scope=GoalsScope -> ZapCloseGoal;
#endif;

Verb 'shake'
    * noun -> Wave;

Verb 'flush'
    * noun -> Empty;

Verb 'find'
    * noun -> Find;

Verb 'locate'
    * noun -> Find;

Verb 'where'
    * 'am' 'i//' -> FindMe
    * 'is'/'are' noun -> Find;

Extend 'look'
    * 'out' noun -> Search;

Extend 'look'
    * 'for' noun -> Find;

Extend 'search'
    * 'for' noun -> Find;

Extend 'fill'
    * noun 'with' noun -> Insert reverse
    * noun 'from' noun -> EmptyT reverse;

Extend only 'sit'
    * 'at' noun -> Enter;

Verb 'vacuum'
    * -> Vacuum;

Extend only 'clean' replace
    * -> Vacuum;

Extend only 'dust' replace
    * -> Vacuum;

Extend only 'sweep' replace
    * -> Vacuum;

Extend 'dig' replace
    * 'in' noun -> Dig
    * noun -> Dig;

Verb 'raise' 'lift'
    * noun -> Raise;

Verb 'lower'
    * noun -> Lower;

Verb 'water'
    * noun -> WaterAction;

Verb 'play'
    * noun -> Play;

Extend 'blow' replace
    * 'on'/'at' noun -> BlowOn;

Verb 'breathe'
    * 'on'/'at' noun -> BlowOn;

Verb 'chant' 'om'
    * -> Sing
    * 'om' -> Sing;

Extend 'sing' replace
    * -> Sing
    * 'om' -> Sing;

Extend 'read' replace
    * noun -> Examine;

[ OptionalString wd count;
    wd = NextWordStopped();
    while (wd ~= 'at' or 'on' or -1) {
        count++;
        wd = NextWordStopped();
    }
    if (count == 0)
        return GPR_FAIL;
    wn--;
    return 0;
];

Verb 'type'
    * -> TypeVague
    * 'on'/'at' noun -> TypeOn
    * OptionalString -> TypeVague
    * OptionalString 'on'/'at' noun -> TypeOn;

Verb 'pour' 'dump'
    * noun 'on'/'onto'/'in'/'into' noun -> EmptyT;

