#charset "us-ascii"
#include <advlite.h>

// The lower level:

//              footOfStairs
//              narrowPassage
//              utilityRoom
//              videoShrink
//              lowerConcourseSouth
//              ollie
//              petShop
//              asteroidBelt
//              lowerConcourseNorth
//              lightFantastic
//              grandmasDrawers
//              cloakRoom
//              stockRoom
//              giftShop
//              hotRags and mazeEntrance are in the file PDboutique.t
//              chartreuseCaboose

//-----------------------------------------------------------------------------
// footOfStairs
//-----------------------------------------------------------------------------

footOfStairs: Room 'The Foot of the Stairs'
    "The area at the foot of the stairs is neither well lighted nor
    inviting. A little trash has blown in from the passage to the
    south, and though the passage itself is a bit gloomy, sunlight beckons
    at the southern end. The stairs themselves lead upward to the north.
    Next to the windowless doorway in the east wall is a small, stark sign that reads
    <q>UTILITIES.</q> Above an open, inviting archway to the northwest is a much
    larger, gaily colored sign that proclaims <q>MORE SHOPS THIS WAY!</q> Through
    the archway you can glimpse the lower concourse. "
    regions = [joeFollowingRegion, ajouMarchingRegion]
    up: TravelConnector, AjouStairs {
        destination = southStairsBottom
        travelBarriers = [wagonBarrier]
    }
    north asExit(up)
    east = utilityRoom
    northwest = lowerConcourseSouth
    south = narrowPassage
;

+ Decoration 'trash'
    "The trash is not important. "
;

+ stairFootArch: Fixture 'inviting archway; (open)'
    "The archway is to the northwest. "
    dobjFor(Enter) asDobjFor(GoThrough)
    dobjFor(GoThrough) {
        verify() {}
        action() {
            doInstead(Go, northwestDir);
        }
    }
;

+ southStairsBottom: StairwayUp, AjouStairs 'stairs;; staircase stairway'
    "The stairs lead upward to the north. "
    destination = arcadeEast
    travelBarriers = [wagonBarrier]
    dobjFor(ClimbUp) {
        action() {
            if ((betsy.getOutermostRoom() == gPlayerChar.getOutermostRoom()) &&
                betsy.curState == betsyClinging)
                betsyClinging.departMessages.doScript();
            inherited();
        }
    }
    dobjFor(Climb) {
        action() {
            if ((betsy.getOutermostRoom() == gPlayerChar.getOutermostRoom()) &&
                betsy.curState == betsyClinging)
                betsyClinging.departMessages.doScript();
            inherited();
        }
    }
;

+ moreShopsSign: ShopSign '+ ; gaily colored more shops this way lettering'
    "The lettering on the MORE SHOPS sign is in a font that's firmly suggestive of fireworks.
    It's quite festive. "
;

//-----------------------------------------------------------------------------
// narrowPassage
//-----------------------------------------------------------------------------

narrowPassage: Room 'Narrow Passage'
    "This narrow, high-ceilinged passageway is well illuminated by an arched opening
    to the south, through which sunlight streams. On the west side of the passage is
    a door above which is mounted a sign that reads <q>PRIMROSE PLUM, VIDEO
    PSYCHIATRIST.</q> Off to the north
    is the lower end of a flight of stairs. "
    regions = [joeFollowingRegion]
    north = footOfStairs
    south = behindBuilding
    west = videoShrinkDoorOutside
;

+ videoShrinkSign: ShopSign 'Video Psychiatrist +; dignified block primrose plum; phd psychiatrist'
    "In dignified block letters, the sign says <q>PRIMROSE PLUM, VIDEO PSYCHIATRIST.</q> "
;

+ videoShrinkDoorOutside: ShopDoorOutside '+; primrose plum psychiatrist video'
    otherSide = videoShrinkDoorInside
;

+ rearArchwayInterior: Fixture 'narrow archway; tall; arch'
    "The archway is a means of egress to the rear of Stufftown. "
    dobjFor(GoThrough) asDobjFor(Enter)
    dobjFor(Enter) {
        verify() { logical; }
        action() {
            doInstead(Go, southDir);
        }
    }
;

//-----------------------------------------------------------------------------
// utilityRoom
//-----------------------------------------------------------------------------

roaringNoise: Noise 'roaring noise; loud constant'
    "The noise of the HVAC blower is nearly deafening. "
    noiseDesc = 'Your ears are assaulted with the sound of the machinery. '
;
    
utilityRoom: Room 'Utility Room'
    "Most of the floor space in this square, windowless room is taken
    up by a large metallic contraption of some sort. Several square
    ducts lead upward from it into the ceiling<<if (roaringNoise.isIn(utilityRoom))>>, and
    the roar of air in the ducts is very loud<<end>>. The only door is an exit to the west. "
    roomBeforeAction() {
        if (gActionIs(Listen) && roaringNoise.isIn(utilityRoom)) {
            doInstead(ListenTo, roaringNoise);
            exit;
        }
    }
    out asExit(west)
    west = footOfStairs
;

+ hvac: Fixture, Switch
    'ventilation unit; metal beige heating aluminum modern electrical air-conditioning large metallic; furnace system heater contraption machine hvac'

    "Ten years ago, you dimly recall, Stufftown's utility room was equipped
    with an old-fashioned (and rather dangerous) gas furnace. That has since
    been replaced by a modern electrical furnace/air-conditioning unit (the usual acronym being HVAC), whose
    side panels are an attractive beige. <<if ((hvacSwitch.isOn) && (junctionBox.isPowered))>>The
    machine is making a loud, constant
    roaring noise, and the side panels are vibrating visibly.<<end>>
    
    <.p>Conveniently situated on the front is a control panel, which is equipped
    with a single switch. "
    isOn = true
    dobjFor(Start) {
        verify() {}
        action() {
            hvacSwitch.makeOn(true);
        }
    }
    dobjFor(Stop) {
        verify() {}
        action() {
            hvacSwitch.makeOn(nil);
        }
    }
    dobjFor (SwitchOn) {
        action() {
            hvacSwitch.makeOn(true);
        }
    }
    dobjFor (SwitchOff) {
        action() {
            hvacSwitch.makeOn(nil);
        }
    }
;

++ sidePanels: Component 'beige side panels;;; them'
    "The side panels are <<if (hvacSwitch.isOn)>>vibrating energetically<<else>>not very interesting<<end>>. "
;

++ hvacControlPanel: Component 'control panel; (hvac) (furnace)'
    "Positioned at the center of the control panel is a large black switch, which can be flipped to either the
    'on' or the 'off' position. Currently the switch is switched <<if (hvacSwitch.isOn)>>on<<else>>off<<end>>. "
        dobjFor(Start) {
        verify() {}
        action() {
            hvacSwitch.makeOn(true);
        }
    }
    dobjFor(Stop) {
        verify() {}
        action() {
            hvacSwitch.makeOn(nil);
        }
    }
    dobjFor (SwitchOn) {
        verify() {}
        action() {
            hvacSwitch.makeOn(true);
        }
    }
    dobjFor (SwitchOff) {
        verify() {}
        action() {
            hvacSwitch.makeOn(nil);
        }
    }
;

+++ hvacSwitch: Switch, Component 'black switch; sturdy plastic'
    "The switch is a sturdy piece of black plastic. It can evidently be switched from the 'on' position to
    the 'off' position. Currently the switch is in the <<if (isOn)>>'on'<<else>>'off'<<end>> position. "
    oiled = nil
    isOn = true
    makeOn(stat) {
        if (!oiled) {
            "No matter how hard you push the switch, it won't budge. It seems to be stuck. ";
            return;
        }
        if (!junctionBox.isPowered) {
            isOn = stat;
            hvac.isOn = stat;
            "Click. You flip the switch to the <<if isOn>>up (presumably, the on)<<else>>down
            (presumably the off)<<end>> position, but nothing happens. ";
            // Here, we're changing the position of the switch, even if it's not powered.
            // However, the file room is now blocked by stuffy, so if the player switches
            // the switch to OFF, powering up the junction box will not make the room unstuffy.
            // The player will have to switch the switch twice to make the file room passable.
            // This is reasonable, I think. Insanely difficult, but reasonable.
            return;
        }
        isOn = stat;
        hvac.isOn = stat;
        // It's now on:
        if (stat) {
            roaringNoise.moveInto(utilityRoom);
            fileRoom.blockedByBlizzard = true;
            fileRoom.blockedByStuffy = nil;
            "The machine starts up with many a clank, a bone-rattling vibration, and soon a roar. ";
        }
        // else it's now off, but it may have been on previously:
        else {
            if (roaringNoise.isIn(utilityRoom)) {
                roaringNoise.moveInto(nil);
                fileRoom.blockedByBlizzard = nil;
                fileRoom.blockedByStuffy = nil;
                "The machine falls silent. ";
            }
            // In normal game play, this should never happen:
            else "Click. Nothing happens. ";
        }
    }
    iobjFor(PourOnto) asIobjFor(PutOn)
    iobjFor(PutOn) {
        verify() {}
        check() {
            if (gDobj != oilCan) "If there's anything at all you can put on the switch, that
                isn't it. ";
        }
        action() {
            doInstead (OilWith, self, oilCan);
        }
    }
    dobjFor(Move) asDobjFor(SwitchVague)
    dobjFor(Push) asDobjFor(SwitchVague)
    dobjFor(Throw) asDobjFor(SwitchVague)
    dobjFor(Oil) {
        verify() {}
        check() {
            if (!oilCan.isDirectlyIn(gPlayerChar)) "You don't seem to be holding an oil can. ";
        }
        action() {
            doInstead (OilWith, self, oilCan);
        }
    }
    dobjFor(OilWith) {
        verify() {}
        check() {
            if (oiled) "You've already oiled the switch. It should move easily now. ";
            else if (gIobj && (!gIobj == oilCan))
                "Applying {the iobj} to the switch is not likely to accomplish anything. ";
        }
        action() {
            oiled = true;
            "<i>Doink-doink.</i> You apply a little oil to the switch. ";
        }
    }
;

++ airDucts: Component 'air ducts; square; duct; them' 
    "The ducts lead up from the furnace into the ceiling. They're covered with
    a thick patina of powdery dust, which has been smudged here and there
    by busy fingers. "
    dobjFor(Clean) {
        verify() {}
        check() {
            "It would take all afternoon, and you'd only get grimy. ";
        }
    }
;

+++ ductDust: Component, Decoration 'thick powdery dust; smudged; numbers smudges patina'
    "On inspecting the layer of dust closely, you discover that somebody
    has doodled some numbers on one of the ducts with a fingertip. The
    numbers are nearly illegible; you might be looking at '8816'
    or '3525,' or possibly '3878.' "
;

//-----------------------------------------------------------------------------
// videoShrink
//-----------------------------------------------------------------------------

videoShrink: Shop 'Psychiatrist\'s Office'
    "This attractively austere little room features a large-screen video monitor, and below it a
    DVD player. Facing the screen is a comfortable-looking chair. Mounted on the wall beside
    the screen is a plaque that says TO FEEL BETTER at the top, and below the plaque is a
    shelf with some dividers. The door through which you entered is to the east. "
    east = videoShrinkDoorInside
    out asExit(east)
;

+ Decoration 'chair; comfortable'
    "The chair is stationed in front of the DVD player. "
;

+ videoShrinkDoorInside: ShopDoorInside '+'
    otherSide = videoShrinkDoorOutside
;

+ shrinkPlaque: Fixture 'feel better plaque'
    "The plaque's text reads as follows: <q>To feel better almost at once, please select
    the appropriate DVD from the shelf, insert it in the DVD player, and then sit back and
    watch the DVD. Dr. Plum's revolutionary method cures any variety of mental illness or
    distress!</q> "
    isReadable = true
    dobjFor (Read) {
        action() {
            doInstead (Examine, self);
        }
    }
;

+ videoShrinkShelf: Surface, Fixture 'shelf;; divider dividers'
    "Along the shelf are a number of labels: Depression, Anxiety, Sexual Dysfunction,
    Schizophrenia, Kleptomania, Agoraphobia, Anger Management, and about a dozen others.
    Unfortunately, none of Dr. Plum's doubtless remarkable DVDs are to be found on the shelf. Possibly someone
    who suffers from kleptomania had a hand in this. "
;

+ shrinkScreen: Fixture 'video monitor; large large-screen; screen'
    "The screen is a couple of meters wide and more than a meter tall. It's mounted on the
    wall. Beneath the screen is a DVD player with a slot and a couple of buttons. "
;

+ shrinkDVDplayer: Fixture, Container, Switch 'DVD player'
    "The DVD player is equipped with a slot into which a disc could be inserted, and a
    button labeled PLAY. "
    proper = nil
    notifyInsert (obj) {
        doInstead(PutIn, obj, dvdSlot);
        
    }
    dobjFor(SwitchOn) {
        action() {
            dvdPlayButton.makePushed();
        }
    }
    isLoaded = nil
    dobjFor(PutIn) {
        verify() {}
        check() {}
        action() {
            doInstead (PutIn, gDobj, dvdSlot);
        }
    }
;

++ dvdSlot: Component, Container 'slot; (dvd) (player)'
    "The slot is in the front of the DVD player. "
    notifyInsert (obj) {
        if (obj != instructionalDVD) {
            "{The dobj} won\'t fit into the slot. ";
            exit;
        }
        else shrinkDVDplayer.isLoaded = true;
    }
    notifyRemove(obj) {
        "You haven\'t seen another DVD player anywhere in Stufftown. Chances
        are, you ought to leave the instructional DVD in the slot. ";
        exit;
    }
;

++ dvdPlayButton: Button, Component 'PLAY button; rubber rubbery dark gray'
    "The button is sort of lozenge-shaped, with rounded corners and a rubbery
    texture. Lettered on the dark gray surface is the word PLAY. "
    dobjFor(Attack) asDobjFor(Push)
    makePushed {
        if (!shrinkDVDplayer.isLoaded) "Nothing happens. ";
        else {
            "The screen lights up, and a cheerful instructor<<one of>> with an unlikely set
            of teeth (they're so perfectly white they almost glow)<<or>><<stopping>> fast-walks
            viewers through an amazingly thorough two-minute explanation of how
            to make wonderful music on any instrument at all. ";
            if (!me.virtuoso) {
                "<.p>As the DVD comes to
                an end in a symphonic flourish --- Beethoven's Ninth, actually, a snippet of the
                triumphal chorus in the last movement --- you feel encouraged to think you now
                qualify as a virtuoso instrumentalist.<<if(batteredTrumpet.isDirectlyIn(me))>> To
                prove it, you rip off
                a few flawless Herb Alpert licks on the trumpet.<<end>> ";
                me.virtuoso = true;
                }
            if (monkeys.getOutermostRoom() == videoShrink) {
                if (monkeys.talented) "The monkeys, however, look bored. ";
                else {
                    monkeys.talented = true;
                    "<.p>The monkeys watch the video, enthralled. Though it seems almost
                    absurd to imagine they could get anything out of the instructor's
                    explanation (since they surely don't understand human speech), as
                    the DVD ends they look at one another, nod sagely, ";
                    if (!monkeys.equipped) {
                        "and waggle their fingers at you. Their gestures, while not entirely
                        coherent, might roughly be translated as, <q>We get it. So where are our
                        musical instruments already?</q> ";
                    }
                    else 
                        "pick up their
                        instruments, and launch into a shockingly professional rendition
                        of <q>Maple Leaf Rag.</q> It's nothing short of miraculous, really. ";
                }
            }
        }
    }
;

//-----------------------------------------------------------------------------
// lowerConcourseSouth
//-----------------------------------------------------------------------------

lowerConcourseSouth: Room 'South End of the Lower Concourse'
    "The lower concourse, broad but with a depressingly low ceiling, stretches
    northward from here toward a brightly lighted glass shopfront at the
    other end. Traditional wood trim gives the walls and doors a faintly
    rustic air, and the gray floor tile is not unattractive.  An open archway leads southeast.
    
    <.p>To the east is a shop door above which the sign reads <q>YOU SHOULDN'T
    HAVE.</q> In the southwest corner
    of the concourse is a shop door above which a sign reads <q>ASTEROID BELT.</q> A
    similar door on the west side of the concourse bears the legend
    <q>FEATHERS, FINS, AND FUR.</q> Standing near the latter is a large glass tank. "
    regions = [joeFollowingRegion, ajouMarchingRegion]
    southeast = footOfStairs
    north = lowerConcourseNorth
    east = shouldntHaveDoorOutside
    west = petShopDoorOutside
    southwest = asteroidDoorOutside
    
    ollieNoticed = nil
;

+ Decoration 'wood trim; nice rustic traditional'
    "The wood trim is nice enough. "
;
+ Decoration 'gray floor tile'
    "The floor tile is not unattractive. Being gray, doubtless it requires
    sweeping less often. "
;

+ asteroidBeltSign: ShopSign 'Asteroid Belt +; leather tanned; sheet'
    "The lettering on the <q>ASTEROID BELT</q> sign is carved into what appears
    to be a single large sheet of tanned leather. (You can almost hear it moo.) "
;

+ feathersFinsFurSign: ShopSign 'Feathers Fins And Fur +; pet; llama ostrich'
    "A variety of unlikely creatures (including what appears to be a llama ... or
    is it an ostrich?) leap, crouch, crawl, and/or fly around the letters of the
    <q>FEATHERS, FINS, AND FUR</q> sign. "
;

+ shouldntHaveSign: ShopSign 'You Shouldn\'t Have +; (gift) daisy daisies'
    "Daisies and more daisies decorate the <q>YOU SHOULDN'T HAVE</q> sign. "
;

+ shouldntHaveDoorOutside: ShopDoorOutside '+; gift you shouldn\'t have'
    otherSide = shouldntHaveDoorInside
;

+ petShopDoorOutside: ShopDoorOutside '+; feathers fins (and) fur'
     otherSide = petShopDoorInside
;


+ asteroidDoorOutside: ShopDoorOutside '+; asteroid belt leather goods shop'
    otherSide = asteroidDoorInside
;

//-----------------------------------------------------------------------------
// ollie
//-----------------------------------------------------------------------------

+ octopusTank: Fixture 'large tank'
    "The tank is a good deal larger than a bathtub --- it\'s almost large enough
    for a midget to swim laps in. Its glass sides haven\'t been cleaned
    lately, and are greenish at the corners with an infestation of algae.
    <<if (tankLid.isIn(remapOn))>>Resting atop the tank is a thick, heavy sheet
    of plywood.<<end>> "
    
    remapOn: SubComponent { }
    remapIn: SubComponent { 
        isTransparent = true
        isOpenable = true
        isOpen = (!tankLid.isIn(lexicalParent.remapOn))
        
        dobjFor(LookIn) {
            preCond = [objVisible]
        }
        lookInDesc = "In the tank <<(ollie.location == self) ? 'are an octopus and' : 'is'>> a
            quantity of greenish water. "
        
        reachBlockedMsg(target) {
            if (tankLid.isIn(self))
                return BMsg (cannot reach, 'The interior of the tank is inaccessible, due to 
                    the presence of the lid. ');
            else
                return BMsg (cannot reach, 'The thick greenish slime in the corners of the tank
                    persuades you that that\'s not such a great idea. ');
        }
        
        iobjFor(PutIn) {
            verify () {
                if (!isOpen) illogical ('To put anything in the tank, you\'d need to
                        remove the lid. ');
            }
            check () {
            "{The subj dobj} would only get wet --- and covered with algae, too. ";
            }
        }
        // This will need to be customized when Betsy becomes available:
        dobjFor(Open) {
            check() {
                if (tankLid.isDirectlyIn(lowerConcourseSouth))
                    "The tank is already open. ";
                else if (betsy.getOutermostRoom() != lowerConcourseSouth)
                    "You have a go at lifting the lid of the tank,
                    but it's far too heavy for you to budge by yourself. ";
                else if (ollie.recaptured) "Do you really want to let the octopus loose again?
                    Probably one escaped octopus per day is plenty. ";
            }
            action() {
                doInstead(Take, tankLid);
            }
        }
        dobjFor(Close) {
            check () {
                if (tankLid.isIn(lexicalParent.remapOn))
                    "The tank is not open. ";
                else "To do that, you would need to replace the lid. ";
            }
        }
        dobjFor(Enter) {
            verify() { illogical ('You\'re kidding, right? ');
            }
        }
    }
;

++ Decoration 'algae; green greenish scummy; scum infestation'
    "The algae is (are?) sort of scummy. "
;
++ Decoration 'glass sides'
    "The glass sides define or delimit the tank. "
    plural = true
;

++ octopusSign: Fixture 'octopus sign'
    "The sign informs passers-by that the tank is home for <q>Ollie the Octopus.</q> "
    subLocation = &remapOn
;

++ tankWater: Fixture 'greenish water; unclean'
    "The water is greenish. It looks unclean. "
    subLocation = &remapIn
;

++ ollie: Actor 'Ollie the Octopus;;; him it'
        "Ollie the Octopus is as large as a medium-sized dog. <<(location == octopusTank.remapIn) ? 'His
        tentacles wave gently, and his dish-sized
        eyes regard you with mild interest' : 'He\'s <i>squidging</i> rapidly
        around the Lower Concourse, rather like a frisky puppy, except
        bigger, built lower to the ground, and leaving a wet trail'>>. "

    dobjFor(Take) asDobjFor(Feel)
    dobjFor(Feel) {
        check () {
            if (isIn(octopusTank.remapIn)) "To do that, you'll need to lift the lid of
                the tank. ";
            else "He's moving too rapidly for you to get near him. Anyway,
                <<if (ray.isIn(lowerConcourseSouth))>>the guards are already doing
                everything possible to catch him. You'd only get in their
                way<<else>>he's probably a bit slimy<<end>>. ";
        }
    }
    initSpecialDesc = "Lounging
        within the tank is an enormous octopus, whose tentacles stir gently, as if
        it\'s waving at you, or perhaps beckoning to you. "
    specialDesc() { 
        if (isIn(octopusTank.remapIn)) initSpecialDesc;
        else
            "Ollie the Octopus is squidging happily around the lower concourse. ";
    }
    subLocation = &remapIn
    // So he'll be listed early when he's loose:
    specialDescOrder = 85
    
    released = nil
    // The release method is called when the tankLid is removed:
    release {
        octopusAch.awardPointsOnce();
        moveInto(lowerConcourseSouth);
        moved = true;
        released = true;
    }
    recaptured = nil
        
;

+++ DefaultCommandTopic
    "The octopus doesn't even blink. Okay, it doesn't have any eyelids, but even if it did
    have eyelids it wouldn't have blinked. Trying to command invertebrates to do things is
    not likely to be productive. "
;

++ tankLid: Thing '(tank) lid; thick plywood heavy; sheet'
    "The lid is a thick sheet of plywood, as long and broad as the tank itself. It\'s
    <<(location == lowerConcourseSouth) ? 'lying on the floor' : 'positioned on
        the top of the tank'>>. "
    specialDesc {
        if (location != octopusTank.remapOn) "The lid of the tank is lying here. ";
    }

    subLocation = &remapOn
    // isListed = (location != octopusTank.subSurface)
    dobjFor(Open) asDobjFor(Take)
    dobjFor(Move) asDobjFor(Take)
    dobjFor(Take) {
        verify() {}
        check() {
            if ((betsy.getOutermostRoom() != lowerConcourseSouth)
                || isDirectlyIn(lowerConcourseSouth))
            "The lid is too heavy for you to lift by yourself. ";
            else if (ollie.recaptured) "Do you really want to let the octopus loose again?
                Probably one escaped octopus per day is plenty. ";
        }
        action() {
            moveInto(lowerConcourseSouth);
            // moved has to be set manually, as the library won't do it when you
            // write your own action() routine for (Take):
            moved = true;
            ollie.release;
            // Now Betsy can follow you into the smokeShop:
            // betsySmallRegion.expandRange();
            // So the Indian will wink at you the next time he sees you:
            woodenIndian.arm();
            "{The betsy}'s compassion for the octopus --- the poor girl
            is practically whimpering --- overcomes both your native caution and
            your natural concern for what may happen to the octopus. You stand at
            one end of the tank, Betsy at the other, and the two of you lift the heavy
            plywood from the tank and set it aside.<.p>
            
            The octopus senses, almost at once, a change in its circumstances. With
            a surprisingly agile and somewhat splashy lurch, it propels itself up over the edge of the
            tank and <i>squidges</i> down on the tile floor with a heavy plop. {The betsy} claps
            her hands and laughs happily.<.p>
            You, on the other hand, suffer immediate misgivings. Possibly the octopus shouldn\'t
            be allowed to roam around. It\'s not likely you\'ll be able to capture it yourself.
            Maybe you ought to tell the security guards about it. ";
        }
    }
;

//-----------------------------------------------------------------------------
// petShop
//-----------------------------------------------------------------------------

petShop: Shop 'Feathers, Fins & Fur'
    "The animal odor in this shop is thick and fusty, but not unpleasant. The clamor of dozens of caged
    animals is nearly deafening. The enormous cage that you vaguely remember from ten years ago is gone;
    in its place, a smaller cage housing a mynah bird occupies the center of the room. A large
    fish tank sits on a long table against one wall.
    
    <.p>The door of the shop
    is to the east. "
    out asExit(east)
    east = petShopDoorInside
;

+ petShopDoorInside: ShopDoorInside '+; pet shop'
    otherSide = petShopDoorOutside
;

+ lowTable: Surface, Fixture 'low table'
    "The low table provides support for the bird cage. "
;

++ mynahCage: OpenableContainer, Fixture '(bird) cage; gilded; bars'
    "The cage is about four feet tall, and sits on a low table. Its gilded bars have a faintly art-deco flavor. "
    isTransparent = true
    contentsListedInLook = nil
    contentsListedInExamine = true
    isOpen = nil
    maxSingleBulk = 20
    dobjFor(LookIn) {
        preCond = [objVisible]
    }
    // hiddenIn = [coin4]
    notifyRemove(obj) {
        if (!mynahBird.distracted) {
            "The mynah bird glares at you and pecks at your hand, nearly drawing blood. You withdraw your
            hand hurriedly. ";
            exit;
        }
        else {
            if (obj == coin4) {
                coin4Ach.awardPointsOnce();
            }
        }
    }
    cannotTakeMsg = 'The cage appears to be firmly bolted to the table. '
    dobjFor(Open) {
        action () {
            makeOpen(true);
            "The cage door creaks as you unlatch it and pull it open. ";
        }
    }
;

+++ Decoration 'shredded newspaper; ; paper shreds scraps scrap'
    "It's shredded. "
;

+++ mynahBird: Thing 'mynah bird; handsome blue brown mixed beady; plumage eye'
    "The mynah has mixed blue and brown plumage and beady black eyes, which
    <<if (distracted)>>are fixed on the singing silver nightingale<<else>>regard
    you skeptically<<end>>. "
    specialDesc = "Pacing restlessly along its perch and occasionally fluffing
        its plumage is a handsome mynah bird. "
    specialDescOrder = 5
    distracted = (canSee(nightingale) && (nightingale.singingCount > 0))
    dobjFor(Take) {
        check() {
            "The mynah bird hops away from your grasping hand and pecks sharply at your fingers. ";
        }
    }
    dobjFor(Release) {
        check() {
            "The mynah seems happy enough in its cage. ";
        }
    }
    dobjFor(Kiss) {
        verify() {}
        check() {
            "You\'d have to stick your head into the cage, and it seems very
            doubtful the bird would respond well. ";
        }
    }
;

// The fishTank, the table on which it sits, and related objects are now in PDhelmet.t


//-----------------------------------------------------------------------------
// asteroidBelt
//-----------------------------------------------------------------------------

Doer 'go east'
    execAction(c) {
        asteroidStairwellDoorInterior.makeLocked(nil);
        asteroidStairwellDoorInterior.makeOpen(true);
        "You push the exit door open and pass through the doorway. Behind you, the
        door closes with a solid <i>thump.</i> ";
        doInstead(Go, eastDir);
        asteroidStairwellDoorInterior.makeOpen(nil);
        asteroidStairwellDoorInterior.makeLocked(true);
    }
    where = asteroidBelt
;

Doer 'open asteroidStairwellDoorInterior'
    execAction(c) {
        "You push the exit door open, but as soon as you let go of it, it closes again. ";
    }
    where = asteroidBelt
;

asteroidBelt: Shop 'Leather Goods Shop'
    "Ah, the smell of fresh leather! This shop is a riot of belts, vests, and boots. Leather
    cowboy hats and leather jewelry crowd the display cases. The exit back to the lower concourse
    is to the northeast, and to the east is a plain-looking door beside which is a sign that
    says <q>STAIRWELL</q>. "
    northeast = asteroidDoorInside
    out asExit(northeast)
    east = asteroidStairwellDoorInterior
;

+ Decoration 'display cases; ; case'
    "The display cases are packed with leather goods. "
    plural = true
;

+ asteroidDoorInside: ShopDoorInside '+; asteroid (belt)'
    otherSide = asteroidDoorOutside
;

+ asteroidStairwellDoorInterior: Door 'exit door'
    "It's a solid-looking door with a crash bar. "
    otherSide = asteroidExitDoorExterior
    checkTravelBarriers(traveler) {
        if (traveler == redWagon) return nil;
        else return inherited(traveler);
    }
;
+ Decoration 'vests; ;cow cowboy cowboys cows fringe fringes vest; them'
    "The vests are embossed with cows and cowboys. They have fringe. "
;
+ Decoration 'cowboy boots; stylish; boot; them'
    "The cowboy boots look darn-tootin' stylish, podner. "
;
+ Decoration 'cowboy hats; ; hat brim brims crown crowns ; them'
    "The cowboy hats have brims and crowns. Nothing surprising, really. "
;
+ Decoration 'leather jewelry; ; pendant pendants'
    "The leather jewelry is not all that charming, actually. "
;

+ Odor 'smell of fresh leather; ; odor fragrance'
    "Ah, the fragrance of fresh leather! "
    smellDesc = "The shop smells powerfully (and pleasantly) of leather. "
;

//-----------------------------------------------------------------------------
// lowerConcourseNorth
//-----------------------------------------------------------------------------

lowerConcourseNorth: Room 'North End of the Lower Concourse'
    "The broad lower concourse of the shopping center stretches southward from here. Three shop doors
    present themselves --- <q>GRANDMA'S DRAWERS</q> on the east, <q>THE LIGHT FANTASTIC</q>
    on the northwest, and <q>HOT RAGS HANG RIGHT</q> on the north. Set in the northeast corner are
    double doors above which a sign reads <q>CHARTREUSE CABOOSE.</q><.p>There's also a
    wide, solid-looking door to the west, beside which is mounted a businesslike sign that says
    <q>SHIPPING & RECEIVING.</q> Beneath the sign a brass plate is set in the wall.

    <.p>In an unconvincing attempt to look modern, the frontage of HOT RAGS HANG RIGHT has broad floor-to-ceiling
    windows on both sides of the door, through which cool light spills onto the concourse. In the windows,
    mannequins strike stylized poses. "
    regions = [joeFollowingRegion, ajouMarchingRegion]
    south = lowerConcourseSouth
    east = grandmasDoorOutside
    north = hotRagsDoorOutside
    northeast = cabooseConcourseDoorOutside
    northwest = lightFantasticDoorOutside
    west = shippingDoorOutside
;

+ hotRagsWindow: Fixture
    'boutique window; broad floor-to-ceiling modern fashion well lighted hot; rags frontage'
    "The window of the boutique is well lighted. Through the window you can see some mannequins. "
    dobjFor(LookThrough) {
        verify() {}
        action() {
            doInstead(Examine, self);
        }
    }
;

+ viewOfMannequins: Distant 'mannequins; trendy stylized; mannequin dummy dummies'
    "The mannequins are trendy, no doubt about that. "
    plural = true
    notImportantMsg = 'The mannequins are on the other side of the glass. '
;

+ grandmaSign: ShopSign 'Grandma\'s Drawers + ; attractive antique furniture wood grain wood-grain'
    "Positioned above the shop door on the east side of the concourse, the <q>GRANDMA\'S DRAWERS</q> sign
    has an attractive wood-grain finish. "
;

+ lightFantasticSign: ShopSign 'Light Fantastic +; illuminated'
    "The <q>LIGHT FANTASTIC</q> sign is illuminated, naturally --- and with such brilliance that
    it could probably be used to signal ships at sea through heavy fog. It's situated above
    the shop door to the northwest. "
;

+ hotRagsSign: ShopSign 'Hot Rags +; fashion boutique hang right'
    "The models on the <q>HOT RAGS</q> sign, though smaller, strike much the same stylized poses as
    the mannequins in the window. "
;

// The question of whether we've already seen this sign and so don't need to read the final comment again
// is stored in the exterior sign:

+ cabooseConcourseSign: ShopSign
    'Chartreuse Caboose +; green bright yellow-green curvaceous feminine kissable pink eyelashes lips nightclub'
    desc {
        "Above the lettering that proclaims this the entrance to the Chartreuse
        Caboose is a cutesy cartoon depicting the last car in a railroad train.
        A bright yellow-green, the curvaceous caboose has long feminine eyelashes
        and kissable pink lips. It seems to be waggling from side to side so
        energetically that its rear wheels have flown clean off the track. ";
        if (!cabooseSign.mentionedAlready) "To the best of your knowledge, railroad cars seldom produce offspring,
        but it can't be ruled out that some sort of mating ritual is being suggested by
        this. ";
        cabooseSign.mentionedAlready = true;
    }
;

+ grandmasDoorOutside: ShopDoorOutside '+; antique furniture grandma\'s drawers'
    otherSide = grandmasDoorInside
;

+ hotRagsDoorOutside: ShopDoorOutside '+; hot rags hang right fashion boutique'
    "The door is pretty much just a sheet of glass, like the broad window in which it\'s set. "
    otherSide = hotRagsDoorInside
;

+ cabooseConcourseDoorOutside: ShopDoorOutside, AjouStairs '+; chartreuse caboose nightclub'
    otherSide = cabooseConcourseDoorInside
;

+ lightFantasticDoorOutside: ShopDoorOutside '+; light fantastic lamp'
    otherSide = lightFantasticDoorInside
;

+ shippingDoorOutside: Door 'shipping and receiving door; wide solid solid-looking'
    "The Shipping & Receiving door is wide and solid-looking. Mounted on the wall
    next to it is a brass plate with a horizontal slot. "
    isOpen = nil
    isLocked = true
    lockability = indirectLockable
    otherSide = shippingDoorInside
//    dobjFor(Open) {
//        check() {
//            if (accessCard.location != lowerShippingSlot) "The door appears to be locked. ";
//        }
//    }
;

+ lowerShippingPlate: Fixture 'brass plate'
    "The brass plate is mounted on the wall next to the Shipping & Receiving door.
    In the plate is a horizontal slot about the right width for a credit card. "
;

 
++ lowerShippingSlot: Component, Container 'horizontal slot'
    "It looks sort of like a slot for a credit card. "
    notifyInsert (obj) {
        if (obj != accessCard) {
            "{The subj dobj} doesn't seem to fit in the slot. ";
            exit;
        }
    }
    iobjFor(PutIn) {
        verify() { logical; }
        check() { 
            if (gDobj != accessCard)
            "{That subj dobj} doesn't seem to fit in the slot. ";
        }
        action() {
            local locked = shippingDoorOutside.isLocked;
            "You insert the cart into the slot. ";
            if (junctionBox.isPowered) {
                shippingDoorOutside.makeLocked(nil);
                if (locked)
                    "A brief buzzing noise suggests that you may have accomplished something. ";
            }
            inherited();
        }
    }     
;

//-----------------------------------------------------------------------------
// lightFantastic
//-----------------------------------------------------------------------------

lightFantastic: Shop 'Lamp Store'
    "Floor lamps, table lamps, hanging lamps, track lighting --- this shop is
    a visual feast for anyone who would rather not be left in the dark. A small
    bare work table sits against one wall. The door is to the southeast. "
    southeast = lightFantasticDoorInside
    south asExit(southeast)
    out asExit(southeast)
;

+ lightFantasticDoorInside: ShopDoorInside '+; (lamp) (light) (fantastic)'
    otherSide = lightFantasticDoorOutside
;

+ lampStoreWorkTable: Fixture 'work table; small square bare wood wooden'
    "The square work table is built of wood. "
;

+ Decoration 'lamps; hanging track gooseneck (table); lamp lighting'
    "Lots of lamps here, yep. When God said, <q>Let there be light,</q> whoever
    owns this store said, <q>I can make a buck on that.</q> "
    plural = true
;

//-----------------------------------------------------------------------------
// grandmasDrawers
//-----------------------------------------------------------------------------

grandmasDrawers: Shop 'Grandma\'s Drawers'
    "The antique furniture store is crowded with glass-front sideboards, armoires, bureaus, umbrella stands,
    grandfather clocks, and knicknack tables of various heights and dimensions. The warm glow of well-oiled
    old wood is nearly palpable. Pride of place obviously goes to the massive, dignified rolltop desk in
    the center of the room.<.p>
    The exit to the concourse is to the west, and an open doorway marked
    <q>Employees Only</q> is in the northeast corner. An open doorway leads south. "
    
    out asExit(west)
    west = grandmasDoorInside
    northeast = cloakRoom
    south = stockRoom
;

+ grandmasDoorInside: ShopDoorInside '+; (antique) (furniture)'
    otherSide = grandmasDoorOutside
;

+ collectibleFurniture: Fixture
    'antique furniture; varied attractive glass-front umbrella love grandfather knicknack; 
    sideboard armoire bureau table stand clock loveseat seat'
    "The antique furniture is varied and attractive, but not, in the last analysis, worth lingering over. Though
    maybe, if you get a tax refund next April, that terribly puritanical, stern-looking New England loveseat....
    If there's anything interesting in the shop, it has to be that rolltop desk. "
    cannotTakeMsg = 'The furniture is far too bulky for you to consider carrying it around. '
;

+ rolltopDesk: Fixture, OpenableContainer 'rolltop desk; rolltop massive dignified burnished; wood'
    "The desk is large and solid-looking. Its wood is burnished to a high gloss. The curved,
    corrugated top can undoubtedly be rolled upward and downward.<<if (isOpen)>> The interior of
    the desk is mostly a mass of cubbyholes.<<end>> "
    isOpen = nil
    dobjFor(Open) {
        action() {
            inherited;
            "The interior of the desk is mainly a mass of small cubbyholes, the kind of
            receptacles in which accountants used to stuff papers, back in the 19th century
            when accountants still used paper. Papers and other small items, possibly. Maybe
            even some pocket change.<<if (parchment.isIn(cubbyHoles))>> Protruding from
            one of the cubbyholes is a rolled-up sheet of parchment.<<end>> ";
        }
    }
    dobjFor(LookIn) {
        verify() {}
        check() {
            if (!isOpen) "You\'ll need to open the desk if you want to search it. ";
        }
        action() { doInstead(LookIn, cubbyHoles); }
    }
    dobjFor(TouchWith) {
        check() {
            if (gIobj && (gIobj != moneyMagnet)) "Nothing obvious happens. ";
        }
        action() {
            if (!isOpen) {
                "There\'s a faint scraping noise from inside the desk. ";
                coin5.moveInto(rolltopDesk);
            }
            else doInstead(AimAt, moneyMagnet, rolltopDesk);
        }
    }
    iobjFor(WaveAt) asIobjFor(AimAt)
    iobjFor(AimAt) {
        verify() {}
        check() {
            if (!isOpen) "Nothing happens. ";
            else if (gDobj != moneyMagnet)
                "The desk remains silent and inert. ";
            else if (cubbyHoles.coinFound)
                "Waving the magnet at the cubbyholes produces no more in the way of coinage. ";
        }
        action() {
            cubbyHoles.coinFound = true;
            coin5.moveInto(rolltopDesk);
            "After a momentary metallic rattling noise, a gold coin pops out of one of the
            cubbyholes! With a little <i>clink,</i> it falls to the interior surface of the desk. ";
        }
    }
;

// The idea is, you wave the moneyMagnet in front of the cubbyHoles, and coin5 pops out and
// lands in the rolltopDesk, where you can pick it up. How should this be implemented?
// My new action is AimAt, which can use 'point at' as a synonym.

++ cubbyHoles: Fixture, RestrictedContainer
    'cubbyholes; little rectangular small; receptacles receptacle holes hole cubbyhole; them'
    "There must be a hundred little rectangular cubbyholes occupying the interior of the desk,
    far too many for you to search one by one.<<if (!coinFound)>>
    For the most part they appear to be empty, but almost anything could be tucked away in
    there, even money! If only you had a way to draw forth something specific....<<end>> "
    allowedContents = []
    coinFound = nil
    dobjFor(TouchWith) {
        check() {
            if (gIobj && (gIobj != moneyMagnet)) "Nothing obvious happens. ";
        }
        action() {
            doInstead (AimAt, moneyMagnet, self);
        }
    }
    // We'll let the iobj handle the game action:
    iobjFor(AimAt) {
        verify() {}
        check() {
            if ((gDobj != moneyMagnet) && (gDobj != flashlight))
                "The cubbyholes remain silent and inert. ";
            else if (coinFound) {
                if (gDobj == moneyMagnet)
                    "Waving the magnet at the cubbyholes produces no more in the way of coinage. ";
                // else it must be the flashlight
            }
        }
        // This is kludgy coding, but if it's the flashlight that's being pointed, the flashlight
        // will produce the action (which is trivial), but we want it to get to the action stage,
        // so we have to test the dobj here:
        action() {
            if (gDobj == moneyMagnet) {
                coinFound = true;
                coin5.moveInto(rolltopDesk);
                "After a momentary metallic rattling noise, a gold coin pops out of one of the
                cubbyholes! With a little <i>clink,</i> it falls to the interior surface of the desk. ";
            }
        }
    }
    dobjFor(LookIn) {
        verify() {}
        check() {
            if (!coinFound) "You paw through the cubbyholes but fail to find anything interesting.
                Still, you can't help thinking you ought to persist. Maybe there's
                something you could do that would be more useful than blindly searching. ";
        }
    }
;

//-----------------------------------------------------------------------------
// cloakRoom
//-----------------------------------------------------------------------------

cloakRoom: Room 'Cloak Room'
    "This shabby, bare little room has little to commend it to your attention.
    There is an open doorway to the south, and a dignified-looking stone archway
    in the north wall, which is presently <<if (cloakRoomArch.blocked)>>blocked by a sheet of gray
    stone<<else>>open<<end>>. "
    south = grandmasDrawers
    out asExit(south)
    southwest asExit (south)
    northeast = cloakRoomArch
    north asExit(northeast)
;

+ cloakRoomArch: OctagonExteriorPassage '+; (black) (north); arrow'
    "Carved into the stone at the summit of the stone arch is a black
    arrow pointing downward.<<if blocked>> At the
    moment, passage through the arch is blocked by a sheet of gray stone.<<else>>
    At the moment, the arch stands invitingly open.<<end>> "
; 

+ cloakRoomHook: Surface, Fixture 'hook'
    "The hook is affixed to the wall of the room. "
;
    
//-----------------------------------------------------------------------------
// stockRoom
//-----------------------------------------------------------------------------

// Far from scaring you away, as happened ten years ago,
// the ghost now won't let you leave. You have to distract him by getting him to look in a mirror.
    
tallMirror: Fixture 'tall mirror; full-length rectangular'
    "The tall rectangular mirror has a narrow wooden frame, and stands upright. "
    cannotTakeMsg = 'The mirror is too heavy for you to lift. '
    dobjFor(LookIn) {
        check() {
            "You look pretty much the way you did an hour ago. ";
        }
    }
;

+ mirrorFrame: Component, Decoration 'narrow wooden frame'
    "The wooden frame is part of the mirror. "
;

stockRoom: Room 'Stock Storage Room'
    "You feel a chilly draft in this dusty and cobweb-festooned chamber. Most of the bulky
    furniture is draped with sheets, except for a round end table standing next to
    an overstuffed easy chair<<if (tallMirror.isIn(stockRoom))>> and a tall rectangular
    mirror<<else>>. One tall rectangular object covered by a 
    sheet catches your eye<<end>>.<.p>There are open doorways to the north and south. "
    north = grandmasDrawers
    south = giftShop
;

+ Decoration 'dust'
    "It looks very much the way dust usually looks. "
;

+ Decoration 'overstuffed easy chair; dusty'
    "The chair is probably very dusty. "
;

+ endTable: Surface, Fixture 'round end table'
    "The round end table stands next to the overstuffed easy chair. "
;

+ bulkyFurniture: Fixture 'bulky furniture'
    "The furniture is covered with sheets, and is of varying sizes and shapes. One tall,
    flat item is situated quite near the south doorway. "
    dobjFor(LookIn) {
        verify() {}
        check() {}
        action() {
            doInstead(LookUnder, furnitureSheets);
        }
    }
    cannotTakeMsg = 'Patently, the furniture is too heavy for you to carry. '
;

+ furnitureSheets: Fixture 'draped sheets; white gray;; them'
    "The sheets (technically white, but in reality varying shades of gray) are
    draped over the bulky furniture. "
    oneTaken = nil
    dobjFor(LookUnder) {
        verify() {}
        check() {
            "You take a peek under a couple of the sheets at random, but find nothing
            of any interest.<<if (!oneTaken)>> Still, you haven't checked under
            all of them....<<end>> ";
        }
    }
    dobjFor(Move) asDobjFor(Take)
    dobjFor(LookIn) asDobjFor(LookUnder)
    dobjFor(Pull) asDobjFor(Take)
    dobjFor(Take) {
        verify() {}
        check() {
            if (oneTaken) "You've already removed the most provocative-looking of the
                sheets. ";
        }
        action() {
            doInstead(Take, mirrorSheet);
        }
    }
;

+ coveredMirror: Fixture 'tall flat object; covered; item'
    "The tall, flat object stands near the south doorway. Like most of the other furniture,
    it's covered by a sheet. "
;

// We want the sheet to have an unlikely plural.
+ mirrorSheet: Fixture 'draped sheet{qqqzzz}; tall flat; item'
    "This particular sheet is draped over the tall, flat object. "
    dobjFor(LookUnder) {
        verify() {}
        action() {
            "Peering under the tall sheet, you see what appears to be a full-length
            mirror. ";
        }
    }
    dobjFor(Pull) asDobjFor(Take)
    dobjFor(Take) {
        verify() {}
        check() {}
        action() {
            furnitureSheets.oneTaken = true;
            coveredMirror.moveInto(nil);
            tallMirror.moveInto(stockRoom);
            tallMirror.seen = true;
            moveInto(nil);
            furnitureSheets.replaceVocab ('draped sheets; white gray; sheet; it them');
            "You whip the sheet away from the tall flat object, revealing a full-length
            mirror. Having no
            further need for the sheet, you fold it and toss it in a corner. ";
        }
    }
;
 
//-----------------------------------------------------------------------------
// giftShop
//-----------------------------------------------------------------------------

giftShop: Shop 'Gift Shop'
    "This brightly lit, almost maniacally cheerful little shop is stuffed with every conceivable type
    of tacky, brightly colored merchandise, not omitting party balloons. The shop door is to the
    west, and an unmarked doorway lies to the north. "
    
    out asExit(west)
    west = shouldntHaveDoorInside
    north = stockRoom
;

+ shouldntHaveDoorInside: ShopDoorInside '+;'
    otherSide = shouldntHaveDoorOutside
;

+ Decoration 'balloons; tacky festive party brightly colored; colors balloon item items merchandise'
    "The balloons are arranged in a tasteful pattern that alternates pink, yellow, green,
    and blue colors. The other items on display are just as voraciously festive. "
    plural = true
;

//-----------------------------------------------------------------------------
// chartreuseCaboose
//-----------------------------------------------------------------------------

// Note: There is another entrance to the Caboose, from the maze. It is not initially visible, however.

caboosePanelCabooseSide: Door 'decorative panel'
    "The decorative panel has swung open, revealing a room
    with mirrors. "
;

chartreuseCaboose: Shop 'Chartreuse Caboose'
    "Subtle indirect lighting gives the impression that night has fallen in this trendy little nightclub, even
    though the sun is still glaring outside. A small stage for a dance band is tucked into one corner.
    <<if (mirrorBall.spinning)>>The mirror ball up near the ceiling is spinning, tossing off shards
    of multicolored light. <<else>>There's a mirror ball hanging from the ceiling. <<end>>
    <.p>There are double doors to the north. Another exit is to the
    southwest<<if (caboosePanelCabooseSide.isOpen)>>, and a decorative panel in the west wall
    has swung open<<end>>. "
    regions = [ajouMarchingRegion]
    north = cabooseExteriorDoorInside
    southwest = cabooseConcourseDoorInside
;

+ cabooseExteriorDoorInside: ShopDoorInside
    'exterior (shop) door; double stairwell; doors exit; it them'
    otherSide = cabooseExteriorDoorOutside
;

+ cabooseConcourseDoorInside: ShopDoorInside
    'mall (shop) door; lower concourse'
    otherSide = cabooseConcourseDoorOutside
;

+ mirrorBall: Distant 'mirror ball'
    "The mirror ball dangles from the ceiling.<<if (spinning)>> It's spinning rapidly and tossing
    off dazzling beams of rainbow light in a pattern that alternates green, pink, blue, and violet.<<end>> "
    spinning = nil
    startSpinning() {
        spinning = true;
    }
    stopSpinning() {
        spinning = nil;
    }
    dobjFor(Feel) {
        verify() { illogical ('It\'s too far away. '); }
    }
;

+ Decoration 'small stage'
    "The stage would be an ideal place for a dance band to set up. "
;

