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


//-----------------------------------------------------------------------------
// inTheTarzan
//-----------------------------------------------------------------------------

// Note: The control panel may later need to be revised to account for the possibility
// that the player has returned to the junction box and maliciously removed a connection.

class TarzanNumberButton: Button, Component
    vocabWords = 'number button; fat gray'
    desc = "It's a fat gray button with the number <<number>> on it. "
    number = -1
    dobjFor(Enter) asDobjFor(Push)
    dobjFor(Push) {
        action() {
            local result = tarzanControlPanel.buttonPushed(number);
            if (!result)
                "You push the <<number>> button, but nothing happens. ";
            else "You push the <<number>> button, and the display
                changes. <<tarzanDisplay.showDisplay>>. ";
        }
    }
    dobjFor(EnterThingOnThing) {
        verify() {}
        check() {
            if ((gIobj != tarzanKeypad) && (gIobj != tarzanControlPanel))
                "You can\'t enter a number on that! ";
        }
        action() {
            doInstead(Push, self);
        }
    }
;
    
inTheTarzan: Room 'In the TARZAN'
    "The interior of the booth is bigger than you'd expect, judging from its
    exterior --- though not, alas, very much bigger. Within the booth is not
    a telephone but what appears to be a control panel. Your recollection
    from ten years ago (mercifully vague, by now) is that this booth, or
    its predecessor, was an unlikely but effective teleportation device.
    Entering a certain combination of digits on the panel and then pressing
    the TRIP button transported you to several unlikely places, none of
    which you're eager ever to visit again. "
    out = travelAgency
    south asExit(out)
;

+ tarzanControlPanel: Fixture 'tarzan control panel;; console'
    "The control panel has a keypad with the numbers 0 through 9 on it, a red button
    beside the keypad marked <q>CLR</q>, a six-digit numeric display, and a blue button
    marked <q>TRIP</q>. "
    isPowered() { return junctionBox.isPowered; }
    buttonPushed(num) {
        if (!isPowered) return nil;
        tarzanDisplay.registerButton(num);
        return true;
    }
    iobjFor(EnterThingOnThing) {
        verify() {}
        check () {
            if (!(gDobj.ofKind(TarzanNumberButton)))
                "You can't enter that on the panel. ";
        }
    }
;

++ tarzanClearButton: Button, Component 'red CLR button; clear rectangular big'
    "The button is red and rectangular. It says <.q>CLR<./q> on it in big white letters. "
    dobjFor(Push) {
        action() {
            if (!tarzanControlPanel.isPowered) {
                "Nothing happens. ";
            }
            else if (tarzanDisplay.digits[6] == '-') {
                "Nothing happens. ";
            }
            else {
                tarzanDisplay.clearDisplay;
                "The display goes blank. It now reads <<tarzanDisplay.showDisplay>>. ";
            }
        }
    }
;

++ tarzanDisplay: Component 'six-digit numeric display;; readout numbers'
    "The display is light gray. <<showDisplay>>. "
    
    showDisplay {
        if (tarzanControlPanel.isPowered == nil) "The display is blank. ";
        else {
            "Currently it reads: ";
            for (local i=1; i<7; i++) {
                say (digits[i]);
            }
        }
    }
    digits = ['-', '-', '-', '-', '-', '-']
    
    registerButton(num) {
        for (local i=2; i<7; i++) {
            digits[i-1] = digits[i];
        }
        local str = getString(num);
        digits[6] = str;
    }
    
    clearDisplay {
        for (local i=1; i<7; i++) {
            digits[i] = '-';
        }
    }
    getString(num) {
        switch (num) {
            case 0: return '0';
            case 1: return '1';
            case 2: return '2';
            case 3: return '3';
            case 4: return '4';
            case 5: return '5';
            case 6: return '6';
            case 7: return '7';
            case 8: return '8';
            case 9: return '9';
            default: return nil;
        }
    }
;

++ tarzanKeypad: Component 'numeric keypad'
    "The numeric keypad has keys numbered 0 through 9 on it. "
    dobjFor(EnterOn) {
        verify() { logical; }
        check() {
            "To take this action, press one numbered button at a time --- for instance,
            PRESS 5." ;
        }
    }
;

+++ button0: TarzanNumberButton '+; 0 zero button; 0 zero'
    number = 0;
+++ button1: TarzanNumberButton '+; 1 one button; 1 one'
    number = 1;
+++ button2: TarzanNumberButton '+; 2 two button; 2 two'
    number = 2;
+++ button3: TarzanNumberButton '+; 3 three button; 3 three'
    number = 3;
+++ button4: TarzanNumberButton '+; 4 four button; 4 four'
    number = 4;
+++ button5: TarzanNumberButton '+; 5 five button; 5 five'
    number = 5;
+++ button6: TarzanNumberButton '+; 6 six button; 6 six'
    number = 6;
+++ button7: TarzanNumberButton '+; 7 seven button; 7 seven'
    number = 7;
+++ button8: TarzanNumberButton '+; 8 eight button; 8 eight'
    number = 8;
+++ button9: TarzanNumberButton '+; 9 nine button; 9 nine'
    number = 9;

++ tarzanTripButton: Button, Component 'blue TRIP button'
    "On the blue button is the important-looking word <q>TRIP</q>. "
    // To go: 563912. To return: 100000:
    testCombi_1 () {
        local combi = ['5', '6', '3', '9', '1', '2'];
        local i;
        for (i=1; i<7; i++) {
            if (combi[i] != tarzanDisplay.digits[i])
                return nil;
        }
        return true;
    }
    testCombi_2 () {
        local combi = ['1', '0', '0', '0', '0', '0'];
        local i;
        for (i=1; i<7; i++) {
            if (combi[i] != tarzanDisplay.digits[i])
                return nil;
        }
        return true;
    }
    codeEval {
        // If the user hasn't entered a full code yet:
        if (tarzanDisplay.digits[1] == '-') return -1;
        local outTheDoor = inTheTarzan.out;
        // First, we test whether you've entered the code for the
        // jungle destination:
        if (testCombi_1) {
            // Are we going to move?
            if (outTheDoor == travelAgency) {
                inTheTarzan.out = jungleClearing;
                "The T.A.R.Z.A.N. hums contentedly, and you have a brief sensation
                of motion. Beyond the doorway you can now see a verdant jungle. ";
                tarzanAch.awardPointsOnce();
                return 1;
            }
            // else we're already there:
            else {
                "The T.A.R.Z.A.N. shudders for a moment, but nothing seems to have
                changed. ";
                return -2;
            }
        }
        else if (testCombi_2) {
            // Are we going home now?
            if (outTheDoor == jungleClearing) {
                inTheTarzan.out = travelAgency;
                "The T.A.R.Z.A.N. makes a soft whooshing noise, and you have
                a brief sense of rapid motion. Beyond the doorway you can see
                the interior of the travel agency. ";
                return 1;
            }
            else {
                "The T.A.R.Z.A.N. beeps softly. Nothing seems to have changed. ";
                return -2;
            }
        }
        // else we've entered a code that is complete but wrong:
        return 0;
    }
    dobjFor(Push) {
        action () {
            // codeEval will return -1 if the display is blank, 0 if
            // the display fails to match a secret code, and 1 if the
            // code is matched. If the code is matched, we've been whisked
            // off to another location (or returned from one).
            local eval = codeEval();
            if ((!tarzanControlPanel.isPowered) || (eval == -1)) {
                "Nothing happens. ";
            }
            else if (eval == 0) {
                "<<one of>>Bzzzt! The booth pulsates around you, the walls seeming to get both
                larger and smaller at the same time in an unsettling rubbery way.
                Confused glimpses of unlikely things flicker before your eyes like
                lightning<<or>>Again the walls pulsate in and out as if they were rubber.
                Glimpses of strange scenes flicker before your eyes<<or>>By now you're
                becoming inured to the unsettling sensations<<or>>Here we go
                again...<<stopping>>.<.p>Briefly you seem to be ";
                randomTrip.doScript();
                " ... but a moment later, reassuringly, you're back in the booth in the Travel Agency. ";
            }
            // If you've moved, codeEval() has already handled it, so there's nothing
            // to do here.
        }
    }
    randomTrip: ShuffledEventList {
    [
        'standing in a trackless desert dotted with tumbling sagebrush,
        under a pitiless glaring sun. Overhead the black dots of vultures wheel ',
        
        'in the grandstand at an old-fashioned rodeo --- yipping cowboys
        and cowgirls, bucking broncos, dust, sweat, the raucous voice of 
        the announcer rasping out of an antiquated P.A. system, the bleachers
        beneath your feet sticky with cotton candy and spilled beer ',
        
        'in a stately high-ceilinged room decorated in the florid style
        of the period of Louis XIV. Men wearing long curly grey wigs and
        shoes with silver buckles are conversing in hushed tones and taking
        snuff, their powdered faces reflected in the gilt-edged mirrors ',
        
        'in an indescribably grimy back alley, after midnight --- overflowing
        dumpsters, broken windows, pools of oily water. Briefly you fancy
        you can smell the reek of the garbage and hear a stray cat yowl ',
        
       'in a deep-green undersea grotto where everything is undulating
        and huge rainbow-colored fish swim serenely past you ',
        
        'floating in the depths of space, with nothing to be seen in any
        direction but the icy flecks of distant stars ',
        
        'on a city street impossibly thronged with people, where huge,
        garish neon signs flash in Asian characters ',
        
        'standing alone at the entrance of an ancient stone temple --- the
        pillars cracked and pitted by age but still proudly erect, and
        beyond it the indescribable blue of the Mediterranean ',
        
        'on an icy mountaintop where bare crags are whipped by gale-force
        winds, and a single step in any direction would send you hurtling
        into the abyss ',
        
       'strapped in a dentist`s chair with a team of three dentists
       pressing close around you. The first dentist --- the least
       dopey-looking but most belligerent one --- has straight black
       hair trimmed across his forehead in a bowl haircut. The second
       dentist has frizzy brown hair that stands up several inches
       behind a high forehead. The third dentist is quite stout, and
       could almost be described as a skinhead if it weren\'t for
       his amiably vacant expression. The first dentist slaps the other
       two and snarls, <q>Spread out!</q> He advances on you brandishing
       an improbably large pair of pliers ',
        
       'floundering in a swimming pool full of rancid-smelling pingpong balls '       
    ]
    }
;

//-----------------------------------------------------------------------------
// jungleClearing
//-----------------------------------------------------------------------------

ocelot: Actor 'hostile ocelot; snarling; cat feline carnivore animal beast'
    "The ocelot is a good deal larger than a house cat, and has a beautiful spotted
    coat. "
    isHostile = true
    makeFriendly() {
        isHostile = nil;
        ocelotAch.awardPointsOnce();
        setState(docileOcelot);
        replaceVocab('docile ocelot; friendly; cat animal beast');
    }
    iobjFor(AimAt) {
        verify() { logical; }
    }
    dobjFor(Feel) {
        verify() {}
        check() {
            if (isHostile) "Only if you want a bunch of long, deep, bloody scratches. ";
        }
        action() {
            "You rub the ocelot's furry tummy, and it purrs contentedly. ";
        }
    }
    iobjFor(BlowAt) {
        verify() {}
    }
    dobjFor(JabWith) {
        verify() { logical; }
        check() {
            if (isHostile) "You can't get close enough to the ocelot to try that. ";
            else "Not a good idea. You'd only put him in a bad mood again. ";
        }
    }
;

+ hostileOcelot: ActorState
    specialDesc = "A hostile ocelot is pacing back and forth, eyeing you distrustfully. "
    stateDesc = "It's snarling at you, displaying a rather alarming set of fangs. "
    isInitState = true
;

+ docileOcelot: ActorState
    specialDesc = "A docile ocelot is lying on its back here, looking up at you in 
        a kittenish way. "
    stateDesc = "It seems to have decided it likes you. It's purring. "
;

jungleClearing: Room 'A Clearing in the Jungle'
    "Warm sunlight spills down among the trees into this broad open space. The
    trees are a riot of green, and form an impenetrable wall except for a
    path that leads off through the jungle to the south. The calls of birds and small animals can be
    heard on all sides. The large blue box through which you traveled to this location stands
    open to the north. "
    
    // This will always be true, if you're here:
    in = inTheTarzan
    north asExit (in)
    south = junglePath
;

+ blueBoxInJungle: Enterable 'blue box; large; tarzan'
    "The blue box is an unlikely transportation device. "
    connector = inTheTarzan
;

/* 
 *   Note: We will probably want the player to be able to try to climb the
 *   trees, swing from or cut the vines, and search or hack her way through the
 *   ferns.
 */

+clearingTrees: Fixture 'trees; tall green impenetrable; wall riot trunks; them'
    "The trees are tall and green and draped with vines. Thick clumps of fern
    fill the spaces between the tree trunks. "
;

+ clearingVines: Decoration 'vines;;; them'
    "The vines sway as they dangle from the trees. "
;

+ clearingFerns: Decoration 'ferns; luxuriant thick; fern clumps; them'
    "The ferns are thick and luxuriant. "
;

+junglePath: PathPassage 'path'
    "The path meanders away through the trees to the south. "
    destination = buddhaClearing
    firstAttempt = true
    canTravelerPass (traveler) {
        if (firstAttempt) {
            ocelot.moveInto(jungleClearing);
            return nil;
        }
        else if (ocelot.isHostile) return nil;
        return true;
    }
    explainTravelBarrier(traveler) {
        if (firstAttempt) {
            firstAttempt = nil;
            "As you start down the path, a spotted feline leaps out of the
            ferns, snarls at you, and brandishes its claws. A hazy memory of college
            zoology suggests that the creature is an ocelot. ";
        }
        else "The ocelot snarls at you, and you back up hurriedly. ";
    }
;

//-----------------------------------------------------------------------------
// buddhaClearing
//-----------------------------------------------------------------------------

buddhaPathEast: PathPassage 'path; (east) eastward'
    "The path created by the statue's thunderous <i>ommmm</i> stretches away
    through the jungle to the east. "
    destination = carpetOfSticks
;
    
buddhaClearing: Room 'In the Presence of a Statue'
    "The jungle surrounds you on all sides, though the path through which you
    arrived leads off to the north. Dominating this sunny open space is an
    enormous statue of a seated Buddha, which gazes serenely off into the east. Brilliant
    sunlight spills down through the jungle canopy. "
    
    north = junglePathNorth
;

+ Decoration 'jungle; sunny open; trees canopy space'
    "The jungle is truly verdant. "
;

+ junglePathNorth: PathPassage 'path; (north) northward'
    "The path meanders away through the trees to the north. "
    destination = jungleClearing
;

// The mirrorSunbeam is in PDthings.

+ clearingSunbeam: Fixture 'sunlight; brilliant blazing; light sunbeam sun pool'
    "The sunlight pours down through the jungle canopy overhead, making the center
    of the clearing a pool of brilliance. "
    iobjFor(PutIn) asIobjFor(HoldIn)
    iobjFor(HoldIn) {
        check() {
            if (!gDobj.isDirectlyHeldBy(me) && (gDobj != makeupMirror)) "You would need to be holding
                {the dobj} in order to hold {that dobj} in the sunlight. ";
            else if (gDobj != makeupMirror) "The sun shines brightly on {the dobj}. ";
        }
    }
    dobjFor(ShineOn) asDobjFor(AimAt)
    dobjFor(AimAt) {
        preCond = [objVisible]
        verify() {
            if (mirrorSunbeam.isIn(nil)) illogical ('You have, at the moment, no
                obvious way of directing the sunlight anywhere. ');
        }
        check() {}
        action() {
            doInstead(AimAt, mirrorSunbeam, gIobj);
        }
    }
    dobjFor(Feel) {
        check() {
            "You hold your hand out in the sunlight. Sure enough, it feels warm. ";
        }
    }
    cannotTakeMsg = 'You have no way to hold onto something as immaterial as sunlight ... but
        you might be able to manipulate it somehow. '
;

+ buddhaStatue: Fixture 'statue of the Buddha; enormous seated; figure'
    "The statue of the Buddha is close to fifteen feet tall. Its stone is a
    bit lichen-encrusted, but otherwise the figure is not a relic; it's in
    good condition. It's seated back among the trees on the west side of the
    clearing, and gazes eastward. In its forehead is what appears to be a
    large jewel, which winks and glistens even in the shade of the trees. "
    roar() {
        if (buddhaPathEast.isIn(buddhaClearing)) {
            "You flash the sunbeam on the jewel again, but other than a beautiful
            display of rainbow colors, nothing happens. ";
            return;
        }
        buddhaAch.awardPointsOnce();
        "As the beam of light hits the jewel in the middle of the statue's
        forehead, the jewel flashes in a coruscating rainbow of brilliant hues.
        Simultaneously, the statue's mouth opens wide, and it emits an astonishingly
        deep and thunderously loud tone. If it were an octave or two higher, it
        might be mistaken for the mystical syllable <i>Om.</i>
        
        <.p>As the sound rolls on, the trees on the east side of the clearing
        bend under its imponderable weight. In short order the trees are bowled
        over and thrust aside, leaving an eastward path through the jungle. ";
        
        buddhaPathEast.moveInto(buddhaClearing);
        buddhaClearing.east = buddhaPathEast;
    }
    iobjFor(ShineOn) asIobjFor(AimAt)
    iobjFor(AimAt) {
        verify() {}
        check() {
            if ((gDobj != mirrorSunbeam) && (gDobj != makeupMirror) && (gDobj != clearingSunbeam))
                "Aiming {the dobj} at the statue would serve
                no obvious purpose. ";
        }
    }
;

++ buddhaJewel: Component 'jewel; large multicolored muted'
    "The jewel is set in the forehead of the statue. Its color, though muted,
    seems constantly to shift as the breeze stirs the fronds that shelter the
    statue from the blazing sun. "
    
    iobjFor(PutOn) {
        verify() {}
        check() {
            if (gDobj != mirrorSunbeam) "You can\'t put anything on the jewel. ";
        }
        action() {
            doInstead (AimAt, mirrorSunbeam, self);
        }
    }
    iobjFor(ShineOn) asIobjFor(AimAt)
    iobjFor(AimAt) {
        verify() {}
        check() {
            if ((gDobj != mirrorSunbeam) && (gDobj != makeupMirror))
                "Aiming {the dobj} at the jewel would serve
                no obvious purpose. ";
        }
    }
;

++ forehead: Decoration, Component 'forehead'
    "The statue\'s forehead is in the usual place, above the eyes. A beautiful
    jewel is embedded in the forehead. "
;

++ lichen: Decoration, Component 'lichen'
    "The lichen has grown here and there on the statue. "
;

//-----------------------------------------------------------------------------
// carpetOfSticks
//-----------------------------------------------------------------------------

carpetOfSticks: Room 'A Carpet of Sticks'
    "Though surrounded on all sides by impenetrable jungle, this clearing is
    far larger than the two you passed through on your way here.
    It's thickly carpeted from edge to edge by a jumbled mass
    of sticks, many of them almost large enough to be called logs. More
    than likely, this wreckage is all that remains of the large
    bamboo structure you discovered ten years ago.
    
    <.p>A pathway leads off through the jungle to the west. "
    
    west = sticksPath
;

+ Decoration 'wreckage; jumbled (bamboo); structure mass sticks logs'
    "The wreckage may once have been part of a large three-dimensional
    maze. "
;

+ sticksPath: PathPassage 'path; (west) westward; pathway'
    "The path leads westward into the jungle. "
    destination = buddhaClearing
;

// -------------------------------------------------------
// The monkeys. Everything here is indented by +
// -------------------------------------------------------

+ monkeys: Actor
    'monkeys; capering hooting grooming nibbling; monkey chimps chimpanzees chimp chimpanzee throng; them'
    "The monkeys (well, okay, they're chimpanzees) are <<if (!equipped)>>engaged in the usual
    monkey business<<else>>brandishing an assortment of musical instruments<<end>>. "
    refuseCommandMsg = 'A few of the monkeys hoot derisively at you, but other
        than that, your attempt to suggest an action seems to have had no effect. No, wait:
        A couple of them are wiggling their fingers at you in an encouraging way.
        Possibly some other form of communication would work better than speech. '
    // They become amenable after being shown the photo of Jane Goodall:
    amenable = nil
    equipped = nil
    talented = nil
    plural = true
    readyToJam() {
        if (getOutermostRoom() != chartreuseCaboose) return nil;
        if (!talented) return nil;
        if (!equipped) return nil;
        return true;
    }
    
    departMessages : ShuffledEventList { 
    [
        { : "The monkeys troop along with you. " }, 
        { : "The monkeys follow you faithfully. " }, 
        { : "The monkeys arrive right on your heels. " }, 
        { : "The monkeys scamper after you, and soon catch up. " },
        { : "The monkeys are distracted for a moment by the scenery, but they soon
            pursue you. " }
    ]}
    // The code that activates the following methods is currently in PDverbs.t.
    // This implements 'sign follow', 'sign wait', etc. But if the PC knows
    // ASL, we'll allow the command to work in the normal way as an option.
    signFollow() {
        if (!me.knowsASL) "The monkeys scratch their chins and shrug at your
            random finger movements. If only there were some way to learn
            a skillful way to do such a thing.... ";
        else if (!amenable) "The monkeys ignore your instruction. ";
        else if (monkeys.curState == monkeysFollowing) "The monkeys are
            already willing to follow you. ";
        else {
            // Code to make the monkeys follow goes here.
            setState(monkeysFollowing);
            monkeyFollowAch.awardPointsOnce();
            startFollowing();
            "The monkeys gather around, ready to follow you. ";
        }
    }
    signWait() {
        if (!me.knowsASL) "The monkeys scratch their chins and shrug at your
            random finger movements. ";
        else if (!amenable) "The monkeys ignore your attempt to give them
            instructions. ";
        else {
            // Code to make the monkeys stop following goes here.
            setState(monkeysWaiting);
            stopFollowing();
            "The monkeys lose interest in following you and settle down. "; 
        }
    }
    firstMusic = true
    signPlay() { 
        if (!me.knowsASL) "The monkeys scratch their chins and shrug at your
            incomprehensible finger movements. ";
        else if ((!amenable) || (!equipped)) "The monkeys ignore your instruction. ";
        else if ((!talented) && (equipped)) "The monkeys indulge in an awful racket with
            their instruments. ";
        else {
            // Code to make the monkeys play their instruments goes here.
            talented = true;
            "<<if (firstMusic)>>The monkeys begin to play. Amazing! Not only are they able to play,
            they seem somehow to have absorbed the tune <q>Take the 'A' Train.</q>
            Their rendition, while not quite up to the standard of the Ellington
            Orchestra, is not bad at all.<<else>>The monkey band belts out a spirited
            rendition of <q><<one of>>It Don't Mean a Thing if It Ain't Got That Swing<<or>>Sunshine
            of Your Love<<or>>Love Me Tender<<or>>Up, Up and Away<<or>>I Shot the Sheriff<<at random>></q>.<<end>> ";
            firstMusic = nil;
        }
    }
    signTake() {
        if (!me.knowsASL) "The monkeys scratch their chins and shrug at your
            incomprehensible finger movements. ";
        else if (equipped) "The monkeys have already picked up their instruments. ";
        else if (getOutermostRoom != tuneTime) "The monkeys have great fun for a minute or
            two picking up random things and putting them back down. ";
        else if (instrumentPile.isIn(nil)) "The monkeys already have their instruments. ";
        else {
            "The monkeys are fascinated by the pile of musical instruments. After a few
            disagreements, each of them
            has selected one of the instruments. They begin to play<<if (talented)>>, belting
            out a rousing chorus of <q>All Along the Watchtower.</q>
            <<else>>, but the unfortunate
            result is a hideous cacophony! After a few moments of confused chagrin, they
            look to you for further guidance.<<end>> ";
            instrumentPile.moveInto(nil);
            carriedInstruments.moveInto(monkeys);
            monkeys.equipped = true;
        }
    }
    noResponseMsg() { monkeyConversationDefault.doScript(); }
;

++ monkeyConversationDefault: DefaultAskTellTopic, ShuffledEventList
    [
        'The monkeys give no sign that they have heard you. ',
        
        'The monkeys seem not to be great conversationalists. ',
        
        'Two or three of the monkeys look at you in vague puzzlement. One of them
        scratches its head and shrugs expressively. ',
        
        'It\'s no use --- the monkeys are not interested in verbal communication. '
    ]
    shuffleFirst = nil
;

++ monkeyGestures: Decoration 'gestures; hand; signals signs gesture sign; them'
    "Though the monkeys' gestures are oddly stylized, you quickly realize the monkeys
    are talking to one another using American Sign Language. <<if (me.knowsASL)>>You're
    able to interpret more than a few of the monkeys' signs. Few of
    the conversations seem to progress much beyond <q>Shove off!</q> and <q>Give me
    that banana!</q>, but there's no doubt the monkeys are conversant with ASL, at
    least to the point of being able to give one another orders. You could probably
    give them commands by signing. But you may need to make friends with them
    first.<<end>> "
    notImportantMsg =
        'There\'s not much you can do with the monkeys\' hand signals, other than
        watch them --- but you might try a few signs of your own. '
;

// The CommandTopics are not embedded in any particular actor state:

++ CommandTopic @Follow
    topicResponse() {
        if (!monkeys.amenable) "The monkeys pretty much ignore you. ";
        else if (!me.knowsASL) "The monkeys look at one another, quite puzzled about
            what you may be trying to communicate. ";
        else if (monkeys.curState == monkeysFollowing) "The monkeys are already
            waiting to follow you. ";
        else {
            setState(monkeysFollowing);
            monkeys.startFollowing();
            "The monkeys gather around, ready to follow you. ";
        }
    }
;

++ monkeysAtHome: ActorState, ShuffledEventList
    stateDesc = "Among other things, they're capering, hooting, grooming, nibbling,
    and smacking their lips. A couple of the bolder ones are tugging importunately
        at your slacks. Others are gesturing at one another in complicated ways.
        The gestures look hauntingly familiar.... "
    specialDesc = "A throng of monkeys is cavorting among the wreckage. "
    isInitState = true
    eventList = [
        'You notice two of the monkeys grooming one another. ',
        
        'One of the monkeys chases another, baring its teeth and shrieking loudly. ',
        
        'One of the monkeys is carrying an infant, which eyes you curiously. ',
        
        'A monkey stands up on its hind legs, hoots, and beats its chest. ',
        
        'One monkey snatches a banana from another and scampers away. ',
        
        'Six of the monkeys seem to be rehearsing an impromptu conga line. '
    ]
    eventPercent = 50
    eventReduceAfter = 6
    eventReduceTo = 25
    shuffleFirst = nil
;

+++ ShowTopic @goodallPhoto
    topicResponse() {
        if (monkeys.amenable) "The monkeys continue to be enthusiastic about
            and admire the photo. ";
        else {
            monkeys.amenable = true;
            "The monkeys crowd around to admire the photo. Several of them
            are moved to touch you in a gentle, friendly way. Evidently they
            recognize Jane Goodall and feel some affection for her. It appears
            you've made some new friends! ";
        }
    }
;

++ DefaultGiveShowTopic
    "The monkeys seem not very interested. They might respond better to a banana,
    but there are no bananas here, and none to be had anywhere in Stufftown. Oh, well. "
;

// Showing the pile of instruments to the monkeys needs to be in both monkeysFollowing
// and monkeysWaiting, so we'll put it in the root directory, in the monkeys object itself.

++ GiveShowTopic @instrumentPile
    topicResponse() {
        "The monkeys are fascinated by the pile of musical instruments. After a few
        disagreements, each of them
        has selected 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. ";
        instrumentPile.moveInto(nil);
        carriedInstruments.moveInto(monkeys);
        monkeys.equipped = true;
    }
;

++ monkeysFollowing: ActorState
    stateDesc = "They cluster around you, waiting for you to lead them
        off on some new adventure.<<if (monkeys.talented)>> A few of them are noodling
        contentedly on their new instruments.<<end>> "
    specialDesc = "At least a dozen monkeys are up to various sorts of monkey business
        in the near vicinity. "
    sayFollowing(oldLoc, conn) {
        monkeys.departMessages.doScript();
    }
;

++ monkeysWaiting: ActorState
    stateDesc = "They're busy grooming one another, examining their surroundings,
        and gesturing at one another. "
    specialDesc = "The monkey troupe is waiting patiently here. "
;


// Since they're still following, I'm not sure we can use this actor state.
++ monkeysPlaying: ActorState
    stateDesc = "They're honking, banging, and plucking contentedly on their new musical instruments. "
    specialDesc = "The Impromptu Monkey Orchestra is blazing away. "
;

// The instruments when they're being carried:

carriedInstruments: Thing 'carried instruments; assorted; assortment instrument; them'
    "The instruments the monkeys are carrying are quite an assortment. "
    dobjFor(Play) {
        verify() {}
        check() {
            "The monkeys will be doing that before long, if all goes well. ";
        }
    }
;
