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

//      The upper level:
//              balcony
//              upperPromenadeEast
//              tuneTime
//              upperPromenadeWest
//              toyShop
//              starkPassage
//              worldHeadquarters
//              marchingOff
//              upperConcourseNorth
//              scrumptiousScrimshaw
//              upperConcourseWest
//              lookingAtYou
//              damagedHallway
//              upperConcourseEast
//              greenFriends
//              secludedLair
//              sportingGoods
//              upperConcourseSouth
//              fillingStation
//              grapeNotion


//-----------------------------------------------------------------------------
// balcony
//-----------------------------------------------------------------------------

Doer 'jump'
    execAction(cmd) {
        "You\'d probably break an ankle. ";
    }
    where = balcony
;

balcony: Room 'Cramped Balcony'
    "There's barely room to turn around on this little balcony, which evidently
    juts from the north side of the tower. A railing around the balcony is plainly
    designed to prevent your climbing down
    to the path below.
    
    <.p>To the east and below lies a rather bedraggled
    walled garden, in which you can see a stone angel<<if (viewOfCardPlayers.location != nil)>>
    and a table at which some men are
    playing cards<<end>>. Directly below you is a path that curves around the north side of
    the tower, terminating at an iron gate on your left.
    
    <.p>The only exit from the balcony
    is the dignified stone arch in the south wall. "
    
    south = balconyArch
;

+ balconyArch: OctagonExteriorPassage '+; (south)'
;

+ Decoration 'path; curving'
    "The path runs beneath you along the northern side of the tower. "
;

+ Decoration 'gate'
    "The gate is below you, off to the left, at the end of the path. "
;

+ viewOfAngel: Distant 'stone angel'
    "The stone angel is gazing down into a pool of water. "
    decorationActions = [Examine, LookAtThrough]
;

+ viewOfPool: Distant 'pool of water'
    "The pool of water lies near the center of the walled garden. "
    decorationActions = [Examine, LookAtThrough]
;

+ viewOfCardPlayers: Distant 'card players;; men; them'
    "The men are seated around a round picnic table, playing cards. They\'re too far away
    for you to make out much in the way of detail. "
    
    decorationActions = [Examine, LookAtThrough]
    // The action of seeing the cheater through the telescope is handled in
    // the brassTelescope object in PDthings.
    dobjFor(LookAtThrough) {
        verify() { logical; }
        check() {
            if (gIobj && (gIobj != brassTelescope)) "You can't very well look at
                the card players through {the iobj}. ";
        }
        action() {
            "<<one of>>Through the telescope you have a clear view of the
            three card players. As you watch the card game in progress, you happen to notice the red-haired
            man slip a card out of his back pocket and add it to his hand. <<brassTelescope.makeCheatingFamiliar>>
            It appears he's cheating! <<or>>The red-haired man does it again. He's definitely cheating. <<stopping>>";
        }
    }
;

+ viewOfTable: Distant 'table; round picnic'
    "The table is in the garden. "
;

+ viewOfGarden: Distant 'walled garden; bedraggled overgrown'
    "In the garden, a stone angel is gazing down into a pool of water.
    <<if playersGroup,isIn(untendedGarden)>> Also, three men
    are sitting around a table playing cards.<<end>> "
    decorationActions = [Examine, LookAtThrough]
;

+ balconyRailing: Fixture 'railing'
    "The railing is high enough to prevent your inadvertently toppling off the edge
    of the balcony, but that would seem to be the extent of its utility. "
;


//-----------------------------------------------------------------------------
// upperPromenadeEast
//-----------------------------------------------------------------------------

upperPromenadeEast: Room 'East End of the Upper Promenade'
    "A long, roofed-over promenade extends westward from here. To the northeast
    is a stone arch leading into a squat and fussy-looking round tower with a
    conical roof<<if (promenadeEastArch.blocked)>>; at the moment, however, the
    stone arch is blocked by a sheet of gray stone<<end>>. Mounted next to the
    tower arch is a sturdy lever.
    <.p>To the north is a stairway leading downward, and to the
    south is a shop door above which is a sign that says <q>TUNE TIME.</q> To
    the southwest is a more modest open archway. Looking out to the north you can see
    over the top of the hedge, beyond which your car and a few others are
    sitting in the parking lot. "
    
    regions = [joeFollowingRegion, ajouMarchingRegion]
    down: TravelConnector, AjouStairs {
        destination = upperPromenadeStairwayDown
        travelBarriers = [wagonBarrier]
    }
    north asExit (down)
    west = upperPromenadeWest
    southwest = starkPassage
    northeast = promenadeEastArch
    south = musicShopDoorOutside
;

+ sturdyLever: Fixture 'sturdy lever'
    "The lever is mounted vertically on the wall of the tower, next to the
    stone arch. "
    dobjFor(Push) {
        verify() { illogical ('The lever cannot be pushed. '); }
    }
    dobjFor(Pull) {
        verify() {}
        action() {
            octagonalRoom.summon();
        }
    }
;

+ Decoration 'hedge'
    "The hedge is down below you to the north. "
;

+ Distant 'cars; parking; car lot'
    "The parked cars are off to the north. "
    plural = true
;

+ ShopSign 'tune time +; wobbly treble backward backwards; notes eighth-notes note flag flags staff'
    "The <q>TUNE TIME</q> sign features a wobbly and imprecise treble staff from which,
    it would appear, some eighth-notes with backwards flags are trying to escape. "
;

+ towerView: Enterable 'tower; squat fussy-looking round'
    "The squat round tower has a conical roof. An archway to the northeast from where you're
    standing <<if (promenadeEastArch.blocked)>>would provide access to the tower,
    if only the archway weren't blocked by a sheet of gray stone<<else>>provides
    access to the interior of the tower<<end>>. "
    connector = promenadeEastArch
;

+ Fixture, Passage 'open southwest archway; ominous mouth arch passage passageway'
    "The rather ominous mouth of the archway leads southwest. "
    destination = starkPassage
;

+ musicShopDoorOutside: ShopDoorOutside '+; music tune time'
    otherSide = musicShopDoorInside
;

+ upperPromenadeStairwayDown: StairwayDown
    'flight of stairs;; stairway staircase; them'
    "The stairway leads down to a gravel-paved path, which is sandwiched between
    a tall hedge on the north and the bulk of Stufftown to the south (directly
    beneath where you're standing. "
    destination = hedgeBorderedPath
;

+ promenadeEastArch: OctagonExteriorPassage '+; white (northeast); arrow doorway'
    "Carved into the stone at the summit of the stone arch is a white
    arrow pointing upward.<<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>> "
;

++ stoneSheet: Component, Decoration 'sheet of gray stone'
    "The gray stone is not important. "
;

++ archArrow1: Component 'white arrow'
    "The white arrow at the summit of the stone arch points upward. "
;

//-----------------------------------------------------------------------------
// tuneTime
//-----------------------------------------------------------------------------

class Instruments: Fixture, Component
    desc = "It looks like a typical <<name>>. "
    dobjFor(Play) {
        preCond = [objVisible]
        verify() {}
        check() {
            if (!me.virtuoso) "You lack the skill. ";
        }
        action() {
            "You whip out a few snappy licks. Wow, that DVD really worked! ";
        }
    }
    dobjFor(Take) {
        verify() {}
        check() {
            "One instrument by itself wouldn't do you much good. And taking the whole
            pile of them would burden you with far more than you could carry. ";
        }
    }
;

tuneTime: Shop 'Tune Time'
    "There's a little of everything musical in the jam-packed music shop, as if the proprietors
    aren't at all sure what their customers will want. Large posters of Igor Stravinsky
    (at his most snobbish) and the rock band Kiss (at their most whatever) adorn the walls. The three B's ---
    balalaikas, berimbaus, and bassoons --- are on sale this week, and a beautiful rosewood player
    piano is prominently on display. In one corner there's also an untidy pile of assorted instruments.
    
    <.p>The door to the shop is to the north. "
    out asExit(north)
    north = musicShopDoorInside
;

+ musicShopDoorInside: ShopDoorInside '+'
    otherSide = musicShopDoorOutside
;

+ instrumentPile: Fixture
    'pile of musical instruments; untidy assorted; ; it them'
    "The instruments are piled in rather an untidy jumble. You notice a clarinet, a piccolo,
    a snare drum, a bassoon, and a violin, among other things. "
    dobjFor(Show) {
        preCond = [objVisible]
    }
    dobjFor(Give) {
        preCond = [objVisible]
    }
    dobjFor(Take) {
        verify() {}
        check() { if (monkeys.getOutermostRoom() != tuneTime) "You'd never be able to carry them all. ";
        }
        // Now we know the monkeys are nearby, and because the pile will disappear when
        // they're equipped, we can safely take and then automatically give them:
        action() {
            instrumentPile.moveInto(nil);
            carriedInstruments.moveInto(monkeys);
            monkeys.equipped = true;
            "The monkeys are fascinated by their new musical instruments. After a few
            brief squabbles, each of them
            is in possession of one of the instruments. They begin to play, but the unfortunate
            result is a hideous cacophony! After a few moments of confused chagrin, they
            look to you for further guidance. ";
        }
    }
;

++ berimbau: Instruments 'berimbau'
;
++ balalaika: Instruments 'balalaika'
;
++ oud: Instruments 'oud'
;
++ xylophone: Instruments 'xylophone'
;
++ violin: Instruments 'violin'
;
++ bassoon: Instruments 'bassoon'
;
++ clarinet: Instruments 'clarinet'
;
++ piccolo: Instruments 'piccolo'
;
++ snareDrum: Instruments 'snare drum'
;

// To do: Make the player piano more interesting. Rolls that can be inserted, pedals that can
// be pumped, etc. Also, those carvings might contain a clue about something.

+ playerPiano: Fixture 'player piano; full-size upright highly polished intricately carved rosewood; case'
    "The player piano, which stands near but not quite up against the wall,
    is a full-size upright. Its case is of highly polished and intricately carved
    rosewood. "
    hiddenBehind = [instructionalDVD]
    dobjFor(Push) asDobjFor(Move)
    dobjFor(Pull) asDobjFor(Move)
    dobjFor(Move) {
        verify() {}
        check() {
            "You give the piano a shove, but it\'s much too heavy. ";
        }
    }
    dobjFor(Take) {
        verify() {}
        check() {
            "You're kidding, right? Carry a piano around? ";
        }
    }
    dobjFor (Play) {
        preCond = [touchObj]
        verify () {}
        action () { "You riffle your fingers lightly across the keys. Though you're by no
            means an accomplished musician, you can tell the piano is dreadfully out of tune. "; }
    }
;

+ stravinskyPoster: Decoration 'Stravinsky poster; snobbish young sternly geeky; igor photo'
    "Stravinsky looks sternly geeky. "
    proper = nil
;

+ kissPoster: Decoration 'Kiss poster; rock cute; band lads'
    "The lads in the poster look quite ferociously cute. "
    proper = nil
;

//-----------------------------------------------------------------------------
// upperPromenadeWest
//-----------------------------------------------------------------------------
    
boxesBarrier: TravelBarrier
    canTravelerPass (traveler, connector) {
        gIobj = playToesExteriorDoorInside;
        return (stackOfBoxes.shovedAside && !playToesExteriorDoorOutside.isLocked);
    }
    explainTravelBarrier (traveler, connector) {
        // if we're outside:
        if (gPlayerChar.getOutermostRoom() == upperPromenadeWest) {
            if (playToesExteriorDoorOutside.isLocked)
                "The door is locked. ";
            else 
                "The door won't budge. It appears there's a stack of cardboard cartons in 
                the toy store blocking the door. ";
        }
        // else we're in the toy store:
        else {
            if (!stackOfBoxes.shovedAside) "The stack of cardboard cartons is in the way. ";
            else "The door is locked. ";
        }
    }
;

upperPromenadeWest: Room 'West End of the Upper Promenade'
    "The roofed-over promenade extends eastward from here. To the south
    is the door of a shop, above which is a sign that reads <q>PLAY TOES REPUBLIC.</q> "
    regions = [joeFollowingRegion]
    east = upperPromenadeEast
    south: TravelConnector {
        travelBarriers = [boxesBarrier]
        destination = playToesExteriorDoorOutside
    }
;

+ ShopSign 'play toes republic +; spinning rubber; toy top duck toys'
    "The <q>PLAY TOES REPUBLIC</q> sign features an assortment of toys, including a
    spinning top and a rubber duck. "
;

+ playToesExteriorDoorOutside: ShopDoorOutside '+; play toes republic toy store shop'
    "The door is made of glass.<<if blocked>> Behind it you can see a tall stack of
    cardboard cartons.<<end>> Mounted beside the door is a small sign that reads, <q>For deliveries,
    please go around and use the back door.</q> "
    otherSide = playToesExteriorDoorInside
    isLocked = true
    blocked = true
    dobjFor(Open) {
        check () {
            if (blocked) "You're unable to open the door, as it's blocked on the
                inside by a towering stack of cardboard cartons. ";
            else inherited();
        }
    }
;

+ boxesFromOutside: Distant 'cardboard cartons;;; them'
    "The cartons are blocking the shop door. "
    decorationActions = [Feel, Push, Take, Move]
    notImportantMsg = 'The cardboard cartons are on the other side of the door. '
    dobjFor(Feel) asDobjFor(Move)
    dobjFor(Push) asDobjFor(Move)
    dobjFor(Take) asDobjFor(Move)
    dobjFor(Move) {
        verify() {}
        check() {
            "To do that, you'd need to be on the other side of the door. ";
        }
    }
;

//-----------------------------------------------------------------------------
// toyShop
//-----------------------------------------------------------------------------

toyShop: Shop 'Play Toes Republic'
    "The toy shop <<toyStoreAch.awardPointsOnce()>>has a new name, but it hasn't changed in any obvious way
    during the past ten years: It's still a riot of brightly colored playthings
    on low tables. It doesn't even seem to be under new management. The six-foot-tall
    stuffed purple dinosaur <<purpleDinosaur.startFollowing>>
    that you recall from before is still here, and still
    evidently in charge of the retail operations.<<one of>> <q>Well, hello there!</q>
    the creature says in a loud, jolly voice when he sees you.<<or>><<stopping>>
    
    <.p>The exterior door to the upper promenade is to the
    north<<if (!stackOfBoxes.shovedAside)>>, but access to the door is blocked
    by a stack of cardboard cartons<<end>>. The door to the
    damaged hallway is to the south, and an open doorway to an interior room is to
    the west. "

    north: TravelConnector {
        travelBarriers = [boxesBarrier]
        destination = playToesExteriorDoorInside
    }
    out asExit(north)
    south = playToesInteriorDoorInside
    west = capsuleRoom
;

+ Decoration 'low tables'
    "The low tables display quite a lot of toys. "
    plural = true
;
+ Decoration 'toys; brightly colored ; playthings'
    "The whole store is a riot of toys. "
    notImportantMsg = 'Other than the little top you may have spotted lying on the floor,
        the toys are not important. '
    plural = true
;

+ playToesExteriorDoorInside: ShopDoorInside '+'
    otherSide = playToesExteriorDoorOutside
;

+ playToesInteriorDoorInside: Door 'hallway door; uninteresting slab; wood'
    "The hallway door is an uninteresting slab of wood. It leads south. "
    otherSide = playToesInteriorDoorOutside
    isLocked = true
    isOpen = nil
;

+stackOfBoxes: Fixture 'stack of cartons; cardboard; box boxes; it them'
    "The cardboard cartons are heaped <<if !shovedAside>>high at the northern side of the room, 
    blocking the door of the shop<<else>>in a corner<<end>>. "
    vocabLikelihood = 20
    shovedAside = nil
    dobjFor(Open) {
        verify() {}
        check() {
            "The store is already well stocked with toys. Whatever you may
            need is bound to be on display already. ";
        }
    }
    dobjFor(Push) asDobjFor(Move)
    dobjFor(Pull) asDobjFor(Move)
    dobjFor(Take) asDobjFor(Move)
    dobjFor(Move) {
        verify() {}
        check() {
            if (shovedAside) "You've already moved the cartons. ";
        }
        action() {
            shovedAside = true;
            boxesFromOutside.moveInto(nil);
            "<q>Oh, I should have moved those myself,</q> the helpful purple
            whatever-it-is says. <q>Here, let me give you a hand!</q> Working
            together, you swiftly move the teetering stack of cartons off to
            one side, thereby removing the impediment to the use of the shop door. ";
        }
    }   
;

//-------------------------------------------------------
// The purple dinosaur:
//-------------------------------------------------------

+ purpleDinosaur: Actor 'purple dinosaur; cheerful puffy; dino barney reptile; it him'
    "The purple dinosaur is rather puffy, as if he were made of a mound of
    purple pillows that had somehow sprung to life, or perhaps some very large marshmallows
    that had been dipped in some quite imaginative food coloring. He's about six feet tall,
    and has wide oval eyes that never, ever blink. "
    friendly = nil
    followDaemon() {
        if (((gPlayerChar.isIn(toyShop)) && (!isIn(toyShop))) || ((gPlayerChar.isIn(capsuleRoom))
            && (!isIn(capsuleRoom))))
            inherited();
    }
;

++ dinoEyes: Component, Decoration 'eyes; wide oval; saucers'
    "The dinosaur's eyes are as big as saucers. "
    plural = true
;

++ AskTopic @purpleDinosaur
    "<q>Oh, I\'m so happy working here!</q> the dinosaur exclaims. <q>I just love the little
    children!<<if (!purpleDinosaur.friendly)>> The only thing that could ever make me happier
    would be if I had some nice jellybeans to eat. I <i>love</i> jellybeans --- yum, yum, yum,
    yum, yum!<<end>></q> "
;

++ AskTellTopic @tJellybeans
    "<q>They\'re just my favorite thing in the whole wide world! Except for watching
    the little kiddies play with toys. But you\'re not a little kiddie, are you? No,
    you\'re a grown-up.</q> "
;

++ AskTopic @capsuleExterior
    "<q>Isn\'t it wonderful?</q> the jolly creature expostulates. <q>It takes you on a
    tour of Stuff World. Stuff World is still in the planning stages, but it\'s going to
    be a true marvel, even better than that park down in Florida, you know the one
    I mean. If you want to see it from the outside instead of the inside, you can
    look at the diorama downstairs in the arcade. The diorama is exactly the same,
    except smaller, you know.</q>
    <.p>The purple thing leans toward you and adds, in a low, confidential tone, <q>If
    you know the right moment to move the lever left or right, you can visit every segment of track
    going both directions! Not at once, I mean --- one after the other. That way
    you can see absolutely everything there is to see. It\'s so clever!</q> "
;

++dinoNearby: ActorState
    specialDesc = "A rather large, friendly-looking purple dinosaur is standing here,
        smiling at you. "
    stateDesc = "He's smiling and nodding at you. "
    isInitState = true
    sayFollowing (oldLoc, conn) {
        followMessages.doScript();
    }
    followMessages: ShuffledEventList {[
        'In following you into the next room, the dinosaur nearly knocks something over. ',
        '<q>Oh, there you are!</q> the helpful purple creature says happily, arriving on
        your heels. ',
        'The purple creature scampers playfully after you. ',
        'The dinosaur is still with you. ',
        'Your purple friend waddles along behind you. ',
        '<q>I\'m so glad you came to see all the wonderful toys,</q> declares the purple
        dinosaur, tiptoeing around you and all but hugging you in his incessant
        (and rather creepy) attempts at friendliness. ',
        'Yes, the purple dinosaur is still with you. ',
        'No, the purple dinosaur has not abandoned you. '
    ]}
;

++ GiveShowTopic @jellybeans
    topicResponse {
        purpleDinosaur.friendly = true;
        jellybeans.moveInto(nil);
        "<q>Oh, jellybeans! Yum-yum-yum-yum-yum!</q> The puffy creature scoops the
        jellybeans out of your hand, stuffs them into his capacious mouth, and makes
        a series of loud, wet munching and smacking noises. <q>Thank you, thank you,
        thank you,</q> he says indistinctly. After swallowing, he goes on, <q>You'll
        be my friend forever!</q> He tries to enfold you in a big puffy hug, but you
        manage to dodge, a maneuver that seems not to have offended him a bit. He's
        still smiling and nodding happily. ";
    }
;

++ AgendaItem
     isReady = (gPlayerChar.isIn(capsuleInterior))
    initiallyActive = true
    invokeItem() {
        "The purple dinosaur leans in the door of the capsule and nods approvingly.
        <q>It's perfectly safe,</q> he assures you, a statement that has an effect precisely
        opposite what he surely intended. <q>Nothing can possibly go wrong while you're in here,
        because you never actually go anywhere. It's a <i>virtual</i> capsule, you see. It's
        to show you how wonderful Stuff World will be when it's built. Just wiggle the lever
        to travel around and see everything. The only thing
        you might want to know is, you won't be able to get out until you steer it back into
        the station. The door will be locked while the capsule is in motion. Okay? Bye-bye! And
        have a wonderful trip!</q> He shuts the door of the capsule with a low thud. ";
        isDone = true;
        capsuleInteriorDoor.makeOpen(nil);
    }
;

//-----------------------------------------------------------------------------
// capsuleRoom is now in a separate file
//-----------------------------------------------------------------------------
    
//-----------------------------------------------------------------------------
// starkPassage
//-----------------------------------------------------------------------------

starkPassage: Room 'Stark Passage'
    "Strictly laid out geometric trim gives this north-south passage
    a neat and logical but not very warm or cozy aspect. On the west
    side of the passage is a shop door above which is a sign that
    reads <q>ATTENTION! MARCHING OFF!</q>
    On the east side is a more official-looking door beside which an
    ostentatiously burnished brass plaque bears the legend <q>STUFF WORLD HEADQUARTERS.</q> "
    regions = [joeFollowingRegion, ajouMarchingRegion]
    north = upperPromenadeEast
    northeast asExit (north)
    south = upperConcourseNorth
    west = marchingOffDoorOutside
    east = headquartersExteriorDoor
;

+ headquartersExteriorDoor: ShopDoorOutside 'door of World Headquarters'
    otherSide = headquartersInteriorDoor
    isOpen = true
    isLocked = nil
;

+ ShopSign 'marching off +; attention military recruitment recruiting jet fighter; tank plane'
    "The <q>ATTENTION! MARCHING OFF</q> sign depicts a tank and a jet fighter plane. "
;

+ Decoration
    'geometric trim; black-and-white dancing long squarish; rows tile swastika tiles motif swastikas'
    "The black-and-white tiles of the trim lay out a squarish motif that bears an
    unfortunate resemblance to long rows of dancing swastikas. "
;

+ brassPlaque: Decoration
    'brass plaque; ostentatious ostentatiously burnished world headquarters'
    "The brass plaque has been polished until it fairly glows. It proclaims that
    the door beside which it is mounted provides access to <q>STUFF WORLD HEADQUARTERS.</q> "
;

+ marchingOffDoorOutside: ShopDoorOutside, AjouStairs '+; attention marching off'
    otherSide = marchingOffDoorInside
;

//-----------------------------------------------------------------------------
// worldHeadquarters
//-----------------------------------------------------------------------------

// I have no idea what this code does. Apparently it allows Joe to press and hold
// a button, but it seems global, not situational.

CommandAction: Action
    actionCommanded = nil
    actorCommanded = nil
;

modify Actor
    handleCommand(action)
    {
        inherited(action);
        gAction = CommandAction;
        gAction.actionCommanded = action;
        gAction.actorCommanded = self;
    }    
;

// The next step is to do a lot better job of tracking which button has been pressed
// or released, and by whom.

class whButton: Button
    'button; small (round) (under); (desk)'
    "The button is small and round. "
    myOwner = 'Flogg'
    notOwner = 'Grabby'
    subLocation = &remapUnder
    isHidden = true
    // code suggested by Eric:
    searchListed = true
    specialDescBeforeContents = true
    
    specialDesc = "Mounted on the underside of the desktop is a small
        button. <<if location.contents.length > 1>>Under the desk you
        can also see <<list of location.listableContents>>. "
    
    useSpecialDesc = (gAction && gActionIs(LookUnder))
    
    // Notice that the necessary actions can happen in either of two ways. You can
    // hold one button and tell Joe to press the other one, or you can tell him to
    // hold one and then press the other. There's also a significant question about
    // whether we need a separate Release action. How many turns will a character be
    // buttonHolding? Maybe the best approach is to use a Fuse in the button itself.
    beforeAction () {
        if (!gActionIn (Hold, Push, CommandAction, Examine, Wait)) {
            if (releaseFuseID) {
                releaseFuseID.removeEvent();
                releaseFuseID = nil;
                if (beingHeldBy == me) "(first removing your finger from the button...)<.p> ";
                beingHeld = nil;
                beingHeldBy = nil;
            }
        }
    }
    beingHeld = nil
    beingHeldBy = nil
    releaseFuseID = nil
    releaseFuse() {
        if (beingHeldBy == me) "Your finger is getting tired, so you let go of the button. ";
        else if (beingHeldBy == joe) {
            if (joe.getOutermostRoom == worldHeadquarters) "The homeless guy lets go of the button. ";
        }
        beingHeld = nil;
        beingHeldBy = nil;
        releaseFuseID = nil;
    }
    dobjFor(Feel) asDobjFor(Push)
    dobjFor(Push) {
        check() {
            // First, have they vacated the office?
            if (flogg.getOutermostRoom == worldHeadquarters) 
                "You can hardly grope around under the desks while Flogg and Grabby are
                here watching you! ";
            // What if this button is already being held?
            else if (beingHeld) {
                if (beingHeldBy == me) "You're already holding the button. ";
                else if (beingHeldBy == joe) "The homeless man is already holding the button. ";
            }
            // Or conversely, what if you're trying to press this one while holding the
            // other one?
            else {
                if (((myOwner == 'Flogg') && (grabbyButton.beingHeldBy == me)) ||
                    ((myOwner == 'Grabby') && (floggButton.beingHeldBy == me))) {
                    "Though you stretch your arms out as far as possible, it proves
                    impossible to reach the button under <<myOwner>>'s desk
                while simultaneously holding the button under <<notOwner>>'s desk. ";
                }
            }
        }
        action() {
            local doorOpened = safeDoor.buttonPressed(self);
            if (!doorOpened)
                "You press the button momentarily, but nothing happens. ";
        }
    }
    dobjFor(Hold) {
        verify() {}
        check() {
            local otherButton;
            if (myOwner == 'Flogg') otherButton = grabbyButton;
            else if (myOwner == 'Grabby') otherButton = floggButton;
            if (otherButton.beingHeldBy == me) "You can't reach the button under <<myOwner>>'s desk
                while simultaneously holding the button under <<notOwner>>'s desk. ";
        }
        action() {
            if (!releaseFuseID) {
                "You press and hold the button under <<myOwner>>\'s desk. ";
                beingHeld = true;
                releaseFuseID = new Fuse (self, &releaseFuse, 5);
                beingHeldBy = me;
                safeDoor.buttonPressed(self);
            }
            else "You're already holding the button. ";
        }
    }
;

// It's not clear this does anything, or needs to. It certainly doesn't print
// anything in response to 'look under flogg's desk'

buttonMsgHandler: CustomMessages
    messages = [
        Msg(look under, '{I} {find} nothing else of interest under {the dobj}. ')  // ,
        // Msg(list contents, '\^ {1} {i} also {see}  ')
    ]
    active = (gActionIs(LookUnder) && ((gDobj == floggDesk.remapUnder) || (gDobj == grabbyDesk.remapUnder)))
;

//-------------------------------------------------------
// The room itself:
//-------------------------------------------------------

worldHeadquarters: Shop 'Stuff World Headquarters' 'office'
    "This well-appointed office is equipped with two broad desks, a mahogany
    desk on the northern<<tFlogg.makeFamiliar()>>
    side of the room<<tGrabby.makeFamiliar()>> and a glass and steel desk on the southern side.
    In the east wall is a solid-looking metal door<<if (safeDoor.isOpen)>>, which
    is standing open<<end>>. The exit is to the west. "
    regions = [joeFollowingRegion, ajouMarchingRegion]
    west = headquartersInteriorDoor
    out asExit(west)
;

+ headquartersInteriorDoor: ShopDoorInside 'office door'
    otherSide = headquartersExteriorDoor
    isOpen = true
    isLocked = nil
;

+ floggDesk: Fixture, Surface 'handsome mahogany desk; (flogg) (flogg\'s) northern broad; recess plaque'
    "The handsome mahogany desk that dominates the northern part of the room sports a tasteful
    plaque that reads <q>Phineas Flogg.</q><<if (flogg.getOutermostRoom() != worldHeadquarters)>> There
    are no desk drawers, oddly enough, just a flat surface and the usual recess under the desk,
    into which one would insert one\'s knees.<<end>> "
    
    dobjFor(LookIn) {
        verify() {}
        action() {
            "Nope --- nothing in the desk. Not even any drawers, just the recess for Flogg\'s knees. ";
        }
    }
    dobjFor(LookBehind) {
        verify() {}
        action() {
            "There\'s nothing behind the desk, but something about it may still be eluding you. ";
        }
    }

    remapOn: SubComponent {}
    remapUnder: SubComponent {
        proper = true
        dobjFor(LookUnder) {
            check() {
                if (flogg.isIn(worldHeadquarters))
                    "Flogg and Grabby probably wouldn't care to see you poking around
                    in their office! ";
            }
            action() {
                floggButton.discover();
                inherited;
            }
        }
    }
    dobjFor(Push) asDobjFor(Move)
    cannotMoveMsg = 'The desk seems to be bolted to the floor. '
    owner = flogg
;

++ floggButton: whButton
    'Flogg\'s button; (mahogany) (desk) (north) small (round) (under); (flogg) (desk)'
    // disambigName = 'flogg\'s button'
    proper = true
    owner = flogg
;

+ grabbyDesk: Fixture 'steel desk; glass glass-and-steel grabby\'s southern broad; plaque'
    "Strategically positioned on the broad glass-and-steel desk that dominates the southern part of the room 
    is a tasteful plaque that reads <q>Eustace
    Grabby.</q><<if (flogg.getOutermostRoom() != worldHeadquarters)>> There
    are no desk drawers, it seems, just a flat desktop and the usual recess under the desk,
    into which one would expect to insert one\'s knees when seated there.<<end>> "
    
    dobjFor(LookIn) {
        verify() {}
        action() {
            "Nope --- there\'s nothing in the desk. Not even any drawers, oddly
            enough, just the usual recess for Grabby\'s knees. ";
        }
    }
    dobjFor(LookBehind) {
        verify() {}
        action() {
            "You find nothing at all behind the desk, but possibly something
            about it may be eluding you. ";
        }
    }

    remapOn: SubComponent {}
    remapUnder: SubComponent {
        proper = true
        dobjFor(LookUnder) {
            check() {
                if (flogg.isIn(worldHeadquarters))
                    "Flogg and Grabby probably wouldn't care to see you poking around
                    in their office! ";
            }
            action() {
                grabbyButton.discover();
                inherited;
            }
        }
    }
    dobjFor(Push) asDobjFor(Move)
    cannotMoveMsg = 'The desk seems to be bolted to the floor. '
    owner = grabby
;

++ grabbyButton: whButton
    'Grabby\'s button; (steel) (desk) (north) small (round) (under); (grabby) (desk)'
    // disambigName = 'grabby\'s button'
    proper = true
    myOwner = 'Grabby'
    notOwner = 'Flogg'
    owner = grabby
;

+ safeDoor: OpenableContainer, Fixture 'metal door; solid wide gray solid-looking featureless safe'
    "<<if (!isOpen)>>The metal door is wide and gray and quite featureless<<else>>Within
    the safe are some shelves<<end>>. "
    isLocked = true
    // vocabLikelihood = -10
    lockability = indirectLockable
    dobjFor(LookBehind) {
        verify() {}
        check() {
            if (!isOpen) "To look behind the metal door, you\'ll need to open it. ";
        }
    }
    dobjFor(Unlock) {
        verify() {}
        check() {
            "The question is how to do that. ";
        }
    }
    dobjFor(LookIn) {
        verify() {}
        check() {
            if (!isOpen) "The metal door seems to be locked, and there's no keyhole
                or combination dial. ";
        }
        action() {
            doInstead(Search, safeShelves);
        }
    }
    // We want this method to return true if it printed anything, or nil otherwise,
    // so that the calling code will know whether it needs to print anything.
    buttonPressed (b) {
        if (((b == floggButton) && (grabbyButton.beingHeld)) ||
            ((b == grabbyButton) && (floggButton.beingHeld)))
        {
            isLocked = nil;
            isOpen = true;
            magnetAch.awardPointsOnce();
            floggButton.beingHeldBy = nil;
            grabbyButton.beingHeldBy = nil;
            
            replaceVocab ('safe; solid wide gray solid-looking; metal door');
            "With a low and definite metallic thump, the metal door springs open. Within
            are some shelves laden with stuff. Evidently, the door is a safe. ";
            if (joe.getOutermostRoom == worldHeadquarters) {
                joe.stopFollowing();
                if (bianca.getOutermostRoom() != behindCaboosePanel) {
                    "<.p>{The joe} says, <q>Was that all you needed me for? Guess so. If you
                    need me for anything else, you know where to find me.</q> He saunters out. ";  
                    // No sense having him follow you around like a puppy:
                    joe.moveInto(footOfPole);
                    joe.followCount = 0;
                }
                // else he has now completed both of his runs:
                else {
                    "<.p>{The joe} says, <q>Was that all you needed me for? Guess so. Catch
                    you later, maybe.</q> He saunters out. ";
                    joe.moveInto(nil);
                }
                return true;
            }
            return true;
            // If ajou is holding the button, we don't need him to do anything.
        }
        else return nil;
    }
;

++ safeShelves: Surface, Fixture 'shelves; laden; shelf; them'
    "The shelves behind the door are piled high with untidy stacks of documents. "
;

+++ safeDocuments: Decoration 'stacks of documents; untidy dull numerous; document stuff; them'
    "The documents are too numerous and obviously far too dull for you to bother with
    them.<<if (!moneyMagnet.moved)>> On one shelf, however, you spy a curious-looking U-shaped piece of
        metal.<<end>> "
;

//-----------------------------------------------------------------------------
// marchingOff
//-----------------------------------------------------------------------------

marchingOff: Shop 'Recruiting Office'
    "This tiny, stuffy room is evidently a recruiting office for the Armed Forces.
    Faded, grimy posters with tattered edges depict the dubious joys of being shot at,
    drowning in a torpedoed submarine, or having one's legs blown off by explosives. "
    east = marchingOffDoorInside
    out asExit(east)
;

+ marchingOffDoorInside: ShopDoorInside, AjouStairs '+'
    otherSide = marchingOffDoorOutside
;

+ Decoration 'posters; faded grimy dubious torpedoed; submarine explosives legs drowning joys'
    "The posters do little to increase one's interest in enlisting in the armed forces. "
;

+ woodenSoldier: Thing 'wooden soldier; painted wood;; him'
    "The wooden soldier is nearly six feet tall; he's not just a toy (though in an emergency
    he might qualify as a boy-toy). He
    looks as if he might have wandered off from a production of Tchaikovksy's
    <i>Nutcracker</i> ballet. His bright red tailored uniform, gold buttons, and tall hat
    would not have been out of place on a 19th century parade ground, although the rosy
    painted cheeks would probably have gotten him in trouble back at the barracks.
    Resting on his shoulder is a rifle, to which is attached a bayonet (also wooden). "
    initSpecialDesc = "Standing in the corner is a life-size painted wooden soldier. "
    isHim = true
    afterAction() {
        inherited();
        if(gActionIn(Play, BlowInto) && (gDobj == batteredTrumpet) && gPlayerChar.virtuoso) {
            "With a dusty creak, the wooden soldier lowers the butt of his rifle to the floor
            and lifts his right arm in a salute. As his arm
            drops, you have the odd sensation that he has become somewhat less wooden than before. On close
            inspection, he appears almost to be breathing. ";
            moveInto(nil);
            ajou.moveInto (marchingOff);
        }
    }
    cannotAttackMsg = 'If you\'re lucky, the soldier won\'t respond by stabbing you with the bayonet. '
    dobjFor(Take) {
        verify() { illogical ( 'The wooden soldier is much too cumbersome for you to burden
            yourself with him. ' ); }
    }
;

++ Component 'tall hat; (soldier\'s)'
    "The tall hat is perched, naturally enough, on top of the soldier's head. "
;
++ Component 'painted cheeks; rosy (soldier\'s);; them'
    "The soldier's rosy cheeks are in the usual location, on either side of his nose. "
;
++ Component 'nose; (soldier\'s)'
    "The soldier's nose is in the usual location, in the center of his face. "
;
++ Component 'uniform; (soldier\'s) 19th century'
    "The soldier's uniform has a double row of gold buttons. "
;
+++ Component 'gold buttons; (soldier\'s) double vertical rows;; them'
    "The gold buttons run in two vertical rows up the front of the uniform. "
;
++ Component 'rifle; (soldier\'s); musket gun'
    "The soldier's rifle, which he holds smartly on his shoulder, is equipped with a bayonet. "
;
+++ Component 'bayonet; (soldier\'s)'
    "The bayonet is attached to the business end of the rifle. "
    iobjFor(CleanWith) asIobjFor(JabWith)
    iobjFor(JabWith) {
        verify() {}
        check() {
            "The wooden bayonet is not useful to you. ";
        }
    }
;
    
//-----------------------------------------------------------------------------
// upperConcourseNorth
//-----------------------------------------------------------------------------

upperConcourseNorth: Room 'North Side of the Upper Concourse'
    "This open, roofed-over passageway looks out over a railing
    to the center of the arcade below, where you can see a large
    diorama laid out on a table. To the northwest is a shop door
    above which a sign proclaims <q>SCRUMPTIOUS SCRIMSHAW.</q>
    
    <<roomFirstDesc()>><.p>You can continue along the concourse
    to the east or west, or pass through an open archway that leads
    north. "
    
    roomFirstDesc {
        if (!firstDescPrinted) {
            firstDescPrinted = true;
            "<.p>A quick visual survey shows that the upper concourse is
            in the shape of an open square. Going west from the north side,
            for instance, will take you to the west side, but to return
            from the west side to the north side you would need to go north, not east. ";
        }
    }
    firstDescPrinted = nil
    regions = [joeFollowingRegion, ajouMarchingRegion]
    north = starkPassage
    east = upperConcourseEast
    west = upperConcourseWest
    northwest = scrimshawDoorOutside
;

+ ShopSign 'scrimshaw +; scrumptious long curving; tooth lettering letters'
    "The lettering of the <q>SCRUMPTIOUS SCRIMSHAW</q> sign is inscribed along
    a very long, curving tooth. "
;

+ viewOfDiorama: Distant 'large diorama;; table'
    "The diorama is too far away for you to make out any detail. "
;

+ scrimshawDoorOutside: ShopDoorOutside '+; scrumptious scrimshaw'
    otherSide = scrimshawDoorInside
;

+ Fixture, Passage 'open archway; ominous; mouth arch passage passageway'
    "The rather ominous mouth of the archway leads north. "
    destination = starkPassage
;

//-----------------------------------------------------------------------------
// scrumptiousScrimshaw
//-----------------------------------------------------------------------------

#ifdef __DEBUG

DefineIAction(CoinsGather)
    execAction(cmd) {
        coin1.moveInto(gPlayerChar);
        coin2.moveInto(gPlayerChar);
        coin3.moveInto(gPlayerChar);
        coin4.moveInto(gPlayerChar);
        coin5.moveInto(gPlayerChar);
        "You now have the five coins. Ka-ching! ";
    }
;
VerbRule(CoinsGather)
    'gather' 'coins'
    : VerbProduction
    action = CoinsGather
    verbPhrase = 'gather/gathering coins'
    missingQ = 'what, me worry'
;

#endif

scrumptiousScrimshaw: Shop 'Scrumptious Scrimshaw'
    "Teeth! Every shelf in this little shop is crammed with teeth, many of them small
    but a few quite bulky. Into each tooth is carved a design of one sort or another.
    The largest item on display, housed in a glass case, is a walrus tusk into which,
    in painstaking detail, has been carved a replica of Leonardo da Vinci's painting
    <q>The Last Supper.</q>
    
    <.p>The door through which you entered is to the southeast.
    
    <.p>Standing in one corner is a rather bulky object, possibly a machine of some
    sort, about the size of a refrigerator. "
    
    out asExit(southeast)
    southeast = scrimshawDoorInside
;

+ Decoration 'teeth'
    "Such a lot of them! "
    plural = true
;

+ Decoration 'glass case'
    "In the glass case is a humongous walrus tusk. "
;

+ Decoration 'last supper; john da vinci humongous walrus; replica cleese painting tusk'
    "To the best of your recollection, it\'s a faithful rendering of <q>The Last
    Supper,</q> although possibly the figure seated next to Jesus looks a bit too
    distinctly like John Cleese. "
;

+ scrimshawDoorInside: ShopDoorInside '+'
    otherSide = scrimshawDoorOutside
;

+ scrimshawDispenser: Fixture
    '(scrimshaw) machine; weathered (ivory) bulky elaborate; dispenser object refrigerator'
    "The machine is the color of weathered ivory, and quite bulky. Affixed to the front is
    a panel at the top of which is the word <q>INSTRUCTIONS.</q> Beside the panel are an LED
    display, three buttons mounted below the display, and a slot that looks to be about the right size
    for coins. Lower down on the front of the machine is a hopper with a lid. "
    powered() { return junctionBox.isPowered; }
    coinsInserted = nil
    toothInHopper = nil
    hopperClosed = true
    carvingSelected = 1
    carvingReady = nil
    afterCarving = nil
    
    coinCount = 0
    receiveCoin() {
        coinCount++;
        "A tiny <i>thunk</i> comes from the interior of the machine. ";
        if (coinCount > 4) {
            coinsInserted = true;
            "The LED flickers for a moment and then displays a new
            message. ";
        }
    }
    redButtonPushed() {
        // carvingReady seems to mean you've already done the carving.
        if ((!coinsInserted) || (carvingReady) || (!powered)) "Nothing happens. ";
        else if ((!hopperClosed) || (scrimshawHopper.contents.car() == nil))
            "A warning buzzer sounds briefly, but nothing else happens. ";
        else if (afterCarving) "The machine makes a dull sighing noise. ";
        // At this point, we know that the coins have been inserted, the hopper
        // is closed, there's something (we don't know what) in the hopper,
        // the carving is not yet ready, and the finished carving has not
        // yet been removed. 
        // This next line should make the game unwinnable, I think, depending on
        // what you've put in the hopper:
        else if ((scrimshawHopper.contents.length() > 1) || (scrimshawHopper.contents.car() != dragonTooth)) {
            "The machine begins to rattle alarmingly, and oily smoke burps out around the
            lid of the hopper. ";
            while (scrimshawHopper.contents.car()) scrimshawHopper.contents.car().moveInto(nil);
        }
        // It appears that the previous line is going to make the game unwinnable. Is this what
        // we want to happen?
        else {
            dragonTooth.addVocab('; ; carving scrimshaw');
            dragonTooth.carve(carvingSelected);
            "The machine hums for a few seconds and emits a low, mellifluous chiming sound, and
            the message in the LED changes to read, <q>Please open hopper and take carving.</q> ";
            carvingReady = true;
            // You can only do this to the tooth once, because you have no more coins.
            coinCount = 0;
        }
    }
    changeSelection(val) {
        if (!powered) "Nothing happens. ";
        else {
            carvingSelected = carvingSelected + val;
            if (carvingSelected < 1) carvingSelected = 19;
            else if (carvingSelected > 19) carvingSelected = 1;
            "The lower line of the LED changes to read: <q>";
            showSelection();
            ".</q> ";
        }
    }
    showSelection () {
        switch (scrimshawDispenser.carvingSelected) {
            case 1: "Vase with flowers"; break;
            case 2: "Old sailor fixing net"; break;
            case 3: "Scenic waterfall with spray"; break;
            case 4: "Lighthouse on rocky island"; break;
            case 5: "Ice skaters on pond"; break;
            case 6: "Beautiful butterfly"; break;
            case 7: "Bull walrus roaring"; break;
            case 8: "Child catching ball"; break;
            case 9: "Clown dancing"; break;
            case 10: "Wolf howling at moon"; break;
            case 11: "Chinese dragon breathing fire"; break;
            case 12: "Ship's anchor with rope"; break;
            case 13: "Radiant sunset with clouds"; break;
            case 14: "Race car speeding along track"; break;
            case 15: "Jazz saxophone player"; break;
            case 16: "Sailing ship tossed by storm"; break;
            case 17: "Wicked witch with hooked nose"; break;
            case 18: "Marlin leaping from sea"; break;
            // In effect, this is #19:
            default: "Tumbling dice showing 6 and 1";
        }
    }
;

++ scrimshawInstructions: Component 'instructions panel; instruction'
    "The instructions on the panel are fairly simple. It reads: <.p><<readDesc>>"
    readDesc = "(1) Insert F&G$5 in slot.<br>(2) Place suitable ivory object in hopper and close
        lid.<br>(3) Select the desired carving from the menu.<br>(4) Press the red button.
        <br>(5) Remove carved object from hopper.<.p>Warning: Do not insert unsuitable
        object in hopper! "
;

++ scrimshawLED: Component 'LED panel; black red letters; display'
    desc {
        "The LED panel is a high-tech component set in the front of the machine. It has
        two rows on which messages can be displayed. ";
        readDesc;
    }
    readDesc { 
        if (scrimshawDispenser.powered) {
            "In red letters the upper row displays the message, <q>";
            if (!scrimshawDispenser.coinsInserted) "Please insert F&G$5.";
            else if (!scrimshawDispenser.toothInHopper) "Please place object to be carved in hopper and close door.";
            else if (!scrimshawDispenser.hopperClosed) "Please close door of hopper.";
            else if (scrimshawDispenser.carvingReady && !scrimshawHopper.isOpen) "Please open hopper door and take carving.";
            else if (scrimshawDispenser.afterCarving) "Thank you for using Newfoundland Enterprises Automatic
                Scrimshaw Carving Technology!";
            // The default ready-message, if none of the other conditions is true:
            else "Please select desired image to be carved, and then press red button.";
            "</q><.p>The lower row reads: <q>";
            scrimshawDispenser.showSelection();
            ".</q> ";
        }
        else "At the moment, it's entirely black. No message is displayed. ";
    }
;

++ scrimshawSlot: Component, Container '(coin) slot'
    "The slot appears to be about the right size to insert a coin. It is set vertically in the front of the
    machine. "
    allowedContents = [coin1, coin2, coin3, coin4, coin5]
    iobjFor(PutIn) {
        verify() { logical; }
        check() {
            // This should reveal whether gDobj is in allowedContents:
            local foundIt = allowedContents.indexOf(gDobj);
            if (!scrimshawDispenser.powered) "On close inspection, you find that a small blank piece of
                metal is blocking the slot. ";
            else if (!gDobj.ofKind(GoldCoin)) "{The subj dobj} {is} too large to fit in the slot. ";
            else if (!foundIt) "It looks quite like a coin slot. Putting other small objects into it
                would probably just jam it up. ";
        }
        action() {
            gDobj.moveInto(nil);
            scrimshawDispenser.receiveCoin();
        }
    }
;

++ scrimshawUpButton: Component, Button 'up button; upward-pointing; arrow'
    "The button is gray. An upward-pointing arrow is inscribed in its face. "
    makePushed () {
        scrimshawDispenser.changeSelection(-1);
    }
;
++ scrimshawDownButton: Component, Button 'down button; downward-pointing; arrow'
    "The button is gray. A downward-pointing arrow is inscribed in its face. "
    makePushed() {
        scrimshawDispenser.changeSelection(1);
    }
;

++ scrimshawRedButton: Component, Button 'red button; bright red'
    "The button is bright red. "
    makePushed() {
        scrimshawDispenser.redButtonPushed();
    }
;

++ scrimshawHopper: Component, OpenableContainer 'hopper'
    "The hopper is mounted on the front of the large machine. "
    bulkCapacity = 75
    notifyInsert(obj) {
        if (obj == dragonTooth) scrimshawDispenser.toothInHopper = true;
    }
    notifyRemove(obj) {
        if (obj == dragonTooth) scrimshawDispenser.toothInHopper = nil;
    }
    makeOpen(stat) {
        inherited (stat);
        if (stat && scrimshawDispenser.carvingReady) {
            scrimshawDispenser.afterCarving = true;
            scrimshawDispenser.carvingReady = nil;
        }
    }
;

++ hopperLid: Component '(hopper) lid'
    "The lid is for opening and closing the hopper. "
    dobjFor(Open) {
        verify() {}
        check() {}
        action() {
            doInstead (Open, scrimshawHopper);
        }
    }
    dobjFor(Close) {
        verify() {}
        check() {}
        action() {
            doInstead (Close, scrimshawHopper);
        }
    }
;

//-----------------------------------------------------------------------------
// upperConcourseWest
//-----------------------------------------------------------------------------

upperConcourseWest: Room 'West Side of the Upper Concourse'
    "From this open, roofed-over walkway, you can look out a railing on the east to
    the center of the arcade below, or climb either up or down a stairway that's
    accessible through a convenient break in
    the railing.
    
    <<upperConcourseNorth.roomFirstDesc()>><.p>Above the door to the southwest is the legend
    <q>HERE'S LOOKING AT YOU.</q> Needless to say, the twin O's in the word <q>looking</q> are
    staring eyeballs. The broad sliding door of an elevator is to
    the west, and to the northwest is an open doorway that appears to lead to a dimly
    lighted hallway. You can also continue north or south, to where the walkway turns corners
    into the north and south sides of the upper concourse. "
    regions = [joeFollowingRegion, ajouMarchingRegion]
    north = upperConcourseNorth
    down: TravelConnector, AjouStairs {
        destination = arcadeStairwayDown
        travelBarriers = [wagonBarrier]
        travelDesc = "(descending the stairs) "
    }
    up: TravelConnector {
        destination = roofStairwayUp
        travelBarriers = [wagonBarrier]
    }
    east asExit(down)
    south = upperConcourseSouth
    southwest = lookingAtYouDoorOutside
    northwest = damagedHallway
    west = upperElevatorDoor
;

+ viewOfArcade: Distant 'center of the arcade; below'
    "The center of the open arcade is below you, beyond the railing. "
;

+ lookingAtYouSign: ShopSign 'Looking at You +; staring; eyeballs eyes eye eyeball'
    "The <q>HERE\'S LOOKING AT YOU</q> sign sports a pair of staring eyeballs. "
;

+ upperElevatorDoor: ElevatorDoorExterior '+'
    otherSide = fakeDoorToNowhere
;

+ upperBrassPlate: SummonButtonPlate '+'
;

//++ upperLevelSlot: SummonPlateSlot '+'
//;

++ upperSummonButton: SummonButton '+'
    myNumber = 3
//    mySlot = upperLevelSlot
;

+ lookingAtYouDoorOutside: ShopDoorOutside '+; looking (at) you optician'
    otherSide = lookingAtYouDoorInside
;

+ arcadeStairwayDown: TravelConnector, AjouStairs, StairwayDown 'descending flight of stairs; (down); staircase stairway; it them'
    "The stairs lead down to the center of the arcade. "
    destination = arcadeCenter
    dobjFor(ClimbDown) {
        action() {
            if ((betsy.getOutermostRoom() == gPlayerChar.getOutermostRoom()) &&
                betsy.curState == betsyClinging)
                betsyClinging.departMessages.doScript();
            inherited();
        }
    }
    
;

+ roofStairwayUp: StairwayUp 'ascending flight of stairs; (up); staircase stairway; it them'
    destination = byTheKiosk
    dobjFor(ClimbUp) {
        action() {
            if ((betsy.getOutermostRoom() == gPlayerChar.getOutermostRoom()) &&
                betsy.curState == betsyClinging)
                betsyClinging.departMessages.doScript();
            inherited();
        }
    }
;

//-----------------------------------------------------------------------------
// lookingAtYou
//-----------------------------------------------------------------------------

lookingAtYou: Shop 'Optician\'s Shop'
    "All manner of optical aids are on display here --- glasses frames, sunglasses,
    mirrors, magnifying glasses, even (oddly) microscopes. A small square table
    stands near the door, which is in the northeast corner of the room. "
    out asExit(northeast)
    northeast = lookingAtYouDoorInside
;

+ lookingAtYouDoorInside: ShopDoorInside '+'
    otherSide = lookingAtYouDoorOutside
;

+ squareTable: Surface, Fixture 'small square table'
    "It's a rather ordinary table. "
;

+ Decoration 'optical aids; glasses magnifying; spectacles frames sunglasses mirrors microscopes mirror microscope; them'
    "It's all very much the assortment of items you'd expect to see in an optician's shop. "
;

//-----------------------------------------------------------------------------
// damagedHallway
//-----------------------------------------------------------------------------

ladderBarrier: TravelBarrier {
    canTravelerPass (t, c) {
        if (t == gPlayerChar)
            return (stepladder.isDirectlyIn(restroom) && stepladder.unfolded);
        else return nil;
    }
    explainTravelBarrier(t, c) {
        if (t == gPlayerChar)
            "There doesn't seem to be a ladder down there. ";
        else "Down through the hole? You're kidding, right? ";
    }
}
;

damagedHallway: Room 'A Damaged Hallway'
    "<<one of>>As you enter this hallway, you very nearly break your neck by stepping into
    <<or>>This short segment of hallway terminates in<<stopping>> a gaping hole, which
    blocks the northward route. Beyond the hole the hallway continues
    a bit further north, where it terminates in a door. The upper concourse is to the south. "
    south = upperConcourseWest
    southwest asExit(south)
    down: TravelConnector {
        destination = hallwayHole
        travelBarriers = [ladderBarrier, wagonBarrier]
    }
    north = "You can't go that way. You'd fall into the hole. "
;

+ viewBelow: Decoration 'room down below'
    "It's hard to be certain, but what's down there looks like the restroom. "
    notImportantMsg = 'If there\'s anything interesting down there, you\'ll have trouble
        getting at it from up here. '
;

+ viewBeyondHole: Distant 'door'
    "The door is beyond the hole. "
;

Doer 'jump'
    execAction(c) {
        doInstead (Go, downDir);
    }
    where = [damagedHallway, damagedHallwayNorth]
;

hallwayHole: MultiLoc, StairwayDown 'gaping hole; ; edge'
    "The hole is as wide as the hallway, which is rather narrow. By peering cautiously
    over the edge, you can see that there's another room down below. "
    destination = restroom
    travelBarriers = [ladderBarrier, wagonBarrier]
    noteTraversal(t) {
        "Moving with great care, you lower yourself down over the edge of the hole.... ";
    }
    locationList = [damagedHallway, damagedHallwayNorth]
    cannotJumpOverMsg = 'It\'s so wide, you can\'t jump over it. ' 
    dobjFor(Enter) {
        verify() {}
        check() {}
        action() {
            doInstead (Go, downDir);
        }
    }
    iobjFor(PutIn) {
        verify() {}
        action() {
            "You drop {the dobj} into the hole, and it falls out of sight. ";
            gDobj.moveInto(restroom);
        }
    }
;

//-----------------------------------------------------------------------------
// damagedHallwayNorth
//-----------------------------------------------------------------------------

Doer 'pull redWagon down'
    execAction (c) {
        "The wagon can only roll on a level surface. ";
        abort;
    }
    where = damagedHallwayNorth
;

class PlayToesButton: Button, Component 'small button; plastic gray'
    desc = "It's a small gray plastic button on which are inscribed the
        letters <q><<myID>></q>. "
    myID = 'XX'
    dobjFor(Push) {
        action() {
            local printIt = playToesInteriorDoorOutside.buttonPressed(myID);
            if (printIt) "Click. ";
        }
    }
;

damagedHallwayNorth: Room 'North of the Hole'
    "The floor seems fairly solid here, though taking any carefree dance steps might be
    inadvisable owing to the gaping hole in the floor. There's a door to the north, on
    which is mounted a keypad. A sign above the door reads <q>BACK DOOR.</q> "
    down: TravelConnector {
        destination = hallwayHole
        travelBarriers = [ladderBarrier, wagonBarrier]
    }
    north = playToesInteriorDoorOutside
;

+ Decoration 'sign'
    "The sign above the door indicates that it\'s the back door. "
;

/* 
 *   The point of this puzzle is that you need to spell 'play toes' backwards.
 *   The 'BACK DOOR' sign is a clue. To make it a little easier, we'll
 *   also spell 'back door' backwards on another set of buttons, as an
 *   alternate solution.
 */

+ playToesInteriorDoorOutside: Door 'hallway door; uninteresting slab; wood'
    "The hallway door is an uninteresting slab of wood. It leads north. "
    otherSide = playToesInteriorDoorInside
    isLocked = true
    isOpen = nil
    lockability = indirectLockable
    indirectLockableMsg = 'There must be a way to do that ... but what could it be? '
    combination1 = ['SE', 'OT', 'YA', 'LP']
    combination2 = ['RO', 'OD', 'KC', 'AB']
    combination3 = ['LP', 'YA', 'OT', 'SE']
    combination4 = ['AB', 'KC', 'OD', 'RO']
    currentInput = ['XX', 'XX', 'XX', 'XX']
    buttonPressed (b) {
        if (!isLocked) {
            "There's probably no need to fiddle any further with the keypad,
            since you've managed to unlock the door. ";
            // don't print "Click":
            return nil;
        }
        currentInput[4] = currentInput[3];
        currentInput[3] = currentInput[2];
        currentInput[2] = currentInput[1];
        currentInput[1] = b;
        if ((currentInput == combination1) || (currentInput == combination2)
            || (currentInput == combination3) || (currentInput == combination4))
        {
            makeLocked(nil);
            makeOpen(true);
            "With a little tinkling sound like nearby chimes, the door swings open. ";
            return nil;
        }
        return true;
    }
;

++ playToesKeypad: Component 'square keypad; ; panel'
    "The keypad is a 4x4 square of small light-gray buttons. Each button has two letters on
    it. Reading from left to right and top to bottom, the four rows look like this:<.p>
    
    AB\ \ \ LP\ \ \ EG\ \ \ OD\b
    OT\ \ \ AI\ \ \ RO\ \ \ HS\b
    YT\ \ \ RL\ \ \ SE\ \ \ NU\b
    GL\ \ \ TE\ \ \ KC\ \ \ YA "
;

+++abButton: PlayToesButton '+; ab'
    myID = 'AB'
;
+++lpButton: PlayToesButton '+; lp'
    myID = 'LP'
;
+++egButton: PlayToesButton '+; eg'
    myID = 'EG'
;
+++odButton: PlayToesButton '+; od'
    myID = 'OD'
;

//OT\ \ \ AI\ \ \ RO\ \ \ HS
+++poButton: PlayToesButton '+; ot'
    myID = 'OT'
;
+++aiButton: PlayToesButton '+; ai'
    myID = 'AI'
;
+++roButton: PlayToesButton '+; ro'
    myID = 'RO'
;
+++hsButton: PlayToesButton '+; hs'
    myID = 'HS'
;

//YT\ \ \ RL\ \ \ SE\ \ \ NU
+++ytButton: PlayToesButton '+; yt'
    myID = 'YT'
;
+++rlButton: PlayToesButton '+; rl'
    myID = 'RL'
;
+++seButton: PlayToesButton '+; se'
    myID = 'SE'
;
+++nuButton: PlayToesButton '+; nu'
    myID = 'NU'
;

//GL\ \ \ TE\ \ \ YR\ \ \ YA
+++glButton: PlayToesButton '+; gl'
    myID = 'GL'
;
+++teButton: PlayToesButton '+; te'
    myID = 'TE'
;
+++kcButton: PlayToesButton '+; kc'
    myID = 'KC'
;
+++yaButton: PlayToesButton '+; ya'
    myID = 'YA'
;

//-----------------------------------------------------------------------------
// upperConcourseEast
//-----------------------------------------------------------------------------

upperConcourseEast: Room 'East Side of the Upper Concourse'
    "This open, roofed-over walkway looks out over a railing on the west side to
    the center of the arcade below. Above a door in the southeast wall is a sign
    that reads <q>RACQUET SCIENCE,</q> and to the northeast
    a door whose sign reads <q>MY GREEN FRIENDS</q> in
    curling vine-like letters. 

    You can go north or south from here, turning a corner into the north or south
    side of the upper concourse. "
    regions = [joeFollowingRegion]
    north = upperConcourseNorth
    south = upperConcourseSouth
    northeast = greenFriendsDoorOutside
    southeast = racquetScienceDoorOutside
;

+ greenFriendsDoorOutside: ShopDoorOutside '+; green friends plant store'
    otherSide = greenFriendsDoorInside
;

+ racquetScienceDoorOutside: ShopDoorOutside '+; racquet science sporting goods store'
    otherSide = racquetScienceDoorInside
;

+ racquetSign: ShopSign 'Racquet Science +; sporting goods tennis golf baseball; club clubs mitt mitts'
    "The <q>RACQUET SCIENCE</q> sign, with its profusion of tennis racquets, golf
    clubs, and baseball mitts, has tilted a bit crooked. "
;
+ greenSign: ShopSign 'My Green Friends +; plant perky potted happy; flower flowers plants'
    "The <q>MY GREEN FRIENDS</q> sign depicts happy flowers and perky potted plants. "
;

//-----------------------------------------------------------------------------
// sportingGoods
//-----------------------------------------------------------------------------

sportingGoods: Shop 'An Abandoned Shop'
    "Though the sign outside the door (<q>Racquet Science</q>) suggests this
    room ought to be a sporting goods store, the interior has been gutted.
    Apparently some new construction is planned, because there are scraps of
    this and that scattered here and there.
    
    <.p>The door back to the upper concourse is to the northwest. "
    out asExit (northwest)
    northwest = racquetScienceDoorInside
;

+ racquetScienceDoorInside: ShopDoorInside '+'
    otherSide = racquetScienceDoorOutside
;

+ constructionScraps: Decoration 'construction scraps; of[prep] this and[weak] that assorted'
    "The assorted construction scraps are not very interesting. "
;

//-----------------------------------------------------------------------------
// greenFriends
//-----------------------------------------------------------------------------

greenFriends: Shop 'Plant Store'
    "A fervid biological odor pervades this crowded shop. Plants large and small
    bedeck every shelf and crowd every aisle. Some are flowering. All are in pots.
    Two cardboard display racks are stocked with plastic bottles. One rack contains
    BioBGone Weed Killer, the other (empty) is dedicated to MiraklGro Plant Food. Near the door is a table
    from which vines droop in profligate profusion.
    
    <.p>The door to the upper concourse is in the southwest corner. To the north is an area that
    seems to be entirely overgrown with plant life. A rickety sign stuck into a random pot at the
    entrance to the northerly area reads, <q>Abandon all caution, ye who enter here!</q> "
    
    southwest = greenFriendsDoorInside
    west asExit(southwest)
    out asExit(southwest)
    north = PM1
;

+ ricketySign: Fixture 'rickety sign'
    "The sign reads, <q>Abandon all caution, ye who enter here!</q> "
    readDesc = "The sign reads, <q>Abandon all caution, ye who enter here!</q> "
;

+ greenFriendsDoorInside: ShopDoorInside '+'
    otherSide = greenFriendsDoorOutside
;

+ bioBGoneRack: Container, Fixture 'BioBGone display rack; cardboard (weed) (killer) (herbicide)'
    "The cardboard BioBGone display rack is tastefully designed with a black-and-white motif of grinning
    skeletons wielding scythes. It's packed with plastic bottles of BioBGone. "
    contentsListed = nil
    proper = nil
    dobjFor(Take) {
        verify() {}
        check() {
            if (bioBGone.moved) "You've already acquired a bottle of BioBGone. ";
        }
        action() {
            doInstead (Take, bioBGone);
        }
    }
;
++ skeletonsMotif: Decoration 'grinning skeletons; black-and-white; motif scythe scythes; them'
    "The grinning skeletons look entirely intimidating enough to frighten a small plant to death. "
;

+ miraklGroRack: Container, Fixture 'MiraklGro display rack; cardboard (plant) (food) (fertilizer)'
    "The cardboard MiraklGro display rack is decorated with cartoonish illustrated plants and flowers in vivid
    primary colors (primarily green). There's no MiraklGro in it, however. "
    contentsListed = nil
    proper = nil
//    dobjFor(Take) {
//        verify() {}
//        check() {
//            if (miraklGro.moved) "You've already acquired a bottle of MiraklGro. ";
//        }
//        action() {
//            doInstead (Take, miraklGro);
//        }
//    }
;
++ plantsIllustration: Decoration 'cartoon (plants); vivid green bulging; muscle muscles; them'
    "The cartoon plants seem actually to have bulging muscles, possibly inspired by Popeye the Sailor Man. "
;

+ decorativePlants: Decoration 'assorted growing plants; large small flowering; vines greenery; them'
    "The plants are wonderful, but not, at the moment, worth lingering over. Not unless you want
    to send Samantha to the prom wearing a cunning design of fig leaves. "
;

+ Decoration 'pots; ; pot'
    "The plants are in the pots. "
    plural = true
;

+ Decoration 'table; ; tables'
    "On the table are a bunch of plants. "
;

+ Decoration 'aisles; ; aisle'
    "The aisles are between the tables. "
    plural = true
;

//-----------------------------------------------------------------------------
// secludedLair
//-----------------------------------------------------------------------------

// The secluded lair is now in the file PDgerbils.t

//-----------------------------------------------------------------------------
// upperConcourseSouth
//-----------------------------------------------------------------------------

upperConcourseSouth: Room 'South Side of the Upper Concourse'
    "This open, roofed-over walkway runs from east to west, turning a corner
    at each end. To the southeast is a door beside which a sign proclaims <q>FILLING
    STATION.</q> To the southwest is another shop door, above which a purple
    sign says, <q>SOMETIMES A GRAPE NOTION.</q> "
    regions = [joeFollowingRegion]
    west = upperConcourseWest
    east = upperConcourseEast
    southeast = fillingStationDoorOutside
    southwest = grapeNotionDoorOutside
;

+ fillingStationDoorOutside: ShopDoorOutside '+; filling station dentist office'
    otherSide = fillingStationDoorInside
;
+ grapeNotionDoorOutside: ShopDoorOutside '+; sometimes (a) grape notion wine'
    otherSide = grapeNotionDoorInside
;

+ ShopSign 'filling station +; dentist toothy cheshire; cat grin'
    "The <q>FILLING STATION</q> sign displays all that's left of the Cheshire Cat,
    namely a very toothy grin. "
;
+ ShopSign 'grape notion +; purple sometimes wine; bottle bottles glass glasses'
    "The <q>SOMETIMES A GRAPE NOTION</q> sign displays not only bunches of grapes
    but bottles and wine glasses. "
;

//-----------------------------------------------------------------------------
// fillingStation
//-----------------------------------------------------------------------------

fillingStation: Shop 'The Filling Station'
    "At first glance, this little room looks rather like a beauty parlor --- a row
    of three padded, tilting chairs, behind which are situated gleaming cabinets full
    of equipment. There are no mirrors, however. A long shelf runs along the rear
    wall, behind the chairs. On the other wall are a price list and a large poster
    depicting an open mouth.
    
    <.p>Mounted on the wall next to the door is a small box.
    
    <.p>The door back to the upper concourse is to the north. "
    
    out asExit(north)
    north = fillingStationDoorInside
    northwest asExit (north)
;

+ Decoration 'padded chairs; tilting seats; ; them'
    "The chairs are about what you\'d expect in a dentist\'s office. "
;

+ Decoration 'price list; root pounds; dollars euros sterling canal prices costs cleaning implant implants'
    "The cost of everything from cleaning to root implants is listed in dollars,
    euros, and pounds sterling. "
;

+ Decoration 'large poster; gaping fabulous; maw mouth set choppers teeth'
    "The poster displays a gaping maw furnished with a fabulous set of choppers. "
;

+ fillingStationDoorInside: ShopDoorInside '+'
    otherSide = fillingStationDoorOutside
;

+ wallBox: Container, Fixture 'wall-mounted box; small'
    "A printed notice on the front of the box says, <q>Dr. Yankem is
    profoundly hearing-impaired.
    Also, during the procedure your mouth will be unavailable. To
    assure that communication during your appointment
    is free of confusion, please take one of these pills.</q> "
    hiddenIn = [aslPill]
;

+ longShelf: Surface, Fixture 'long shelf'
    "The long shelf is situated on the wall beneath the glass cabinets. "
;
     
+ dentistCabinets: LockableContainer, Fixture 'glass cabinets; (equipment) (dentist); cases doors case cabinet; them'
    "The cabinets behind the chairs have glass doors. Within them are neat arrays of little
    tools. "
    contentsListed = nil
    contentsListedInExamine = nil
    // examineStatus = nil
    isLocked = true
    isTransparent = true
    lockability = indirectLockable
    reachBlockedMsg(target)
    {
        local obj = self;
        gMessageParams(obj);
        gMessageParams(target);
        return  BMsg(cannot reach, '{I} {can\'t} reach {the target} through
            the glass doors of {the obj}. ');
    }
;

++ dentistEquipment: Decoration
    'little tools; (dentist) hypodermic; equipment tool drills scrapers drill scraper syringe syringes; them'
    "The tools are about what you'd expect a dentist to need --- drills, scrapers,
    hypodermic syringes, and so on. "
;

//-----------------------------------------------------------------------------
// grapeNotion
//-----------------------------------------------------------------------------

Doer 'go east'
    execAction(c) {
        grapeNotionExitDoorInterior.makeLocked(nil);
        grapeNotionExitDoorInterior.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);
        grapeNotionExitDoorInterior.makeOpen(nil);
        grapeNotionExitDoorInterior.makeLocked(true);
    }
    where = grapeNotion
;

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

grapeNotion: Shop 'Wine Shop'
    "Racks of dusty wine bottles crowd this little shop. The
    door back to the upper concourse is to the north. Above a more businesslike door
    to the east is a sign that says <q>EXIT.</q> "
    
    north = grapeNotionDoorInside
    northwest asExit(north)
    east = grapeNotionExitDoorInterior
;

+ grapeNotionDoorInside: ShopDoorInside
    otherSide = grapeNotionDoorOutside
;

+ grapeNotionExitDoorInterior: Door 'exit door'
    "It's a solid-looking door with a crash bar. "
    otherSide = grapeNotionExitDoorExterior
    checkTravelBarriers(traveler) {
        if (traveler == redWagon) return nil;
        else return inherited(traveler);
    }
;

+ cheapCartons: Decoration 'unopened cartons; large cheap screw-top varietal; boxes; them'
    "The cartons of cheap wine are not something you're inclined to bother with. "
;

+ bottleRacks: Fixture 'racks; (wine) diagonal dusty; dust rack slats; them'
    "The racks are constructed in the usual fashion, of diagonal wood slats, and are
    well stocked with wine bottles. "
;

++ Decoration 'bottles; (wine) numerous dark (dusty)'
    "The wine bottles are numerous, dark, and dusty. "
    plural = true
;

