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

// We want Joe to be able to follow you to the following rooms:
// behindBuilding, footOfPole (to be tidy), 
// narrowPassage, footOfStairs,
// lowerConcourseSouth, lowerConcourseNorth, hotRags, mazeEntrance,
// behindCaboosePanel (to be tidy),
// arcadeEast, arcadeCenter,
// arcadeWest (because why not?), arcadeNorth,
// archedPassage,
// mainEntrance, hedgeBorderedPath,
// upperPromenadeEast,
// upperPromenadeWest (because why not?), starkPassage,
// worldHeadquarters, upperConcourseNorth, upperConcourseWest,
// upperConcourseEast, upperConcourseSouth

joeFollowingRegion: Region
    regionBeforeTravel(traveler, connector) {
        if (traveler != joe) return;
        local foundIt = roomList.indexOf(connector);
        if (foundIt) {
            "<q>I don't think I ought to go there,</q> {the joe} says. ";
            abort;
        }
    }
;

flyingPixies: Thing
    'some pixies; mischievous little tiny winged vaguely human; fairy fairies pixie pixy sprite sprites; them'
    desc() {
        "The pixies are tiny, winged, and vaguely human in appearance. They're ";
        if (flying) "darting around {the joe}'s face";
        else if (capturedButAwake) "buzzing angrily within the folds of the scarf";
        else if (groggy) "so groggy they're barely moving";
        ". ";
    }
    flying = true
    capturedButAwake = nil
    groggy = nil
    dobjFor(Release) {
        verify() {}
        check() {
            if (flying) "The pixies are not confined, and thus not in need of release. ";
            else "After all the trouble you\'ve gone to to capture
                them? ";
        }
    }
    dobjFor(Catch) {
        verify() { logical; }
        check() {
            if (!flying) "You've already caught them. ";
            else if (!scarf.isDirectlyIn(gPlayerChar))
                "You try to grab them, but they easily evade your grasping fingers. ";
            else if (scarf.isWornBy(gPlayerChar))
                "In the scarf, perhaps? Not while you're wearing it. ";
        }
        action() {
            moveInto(scarf);
            capturedButAwake = true;
            flying = nil;
            scarf.containsPixies = true;
            "You unfurl the scarf and scoop up the whole tribe of flying pixies
            in its folds. They squirm and buzz angrily, but by golly you've trapped
            them! Whether they'll stay trapped for more than a few seconds is not,
            at this moment, entirely clear, as they've gone into a huddle to work
            out an escape plan. ";
        }
    }
    dobjFor(Take) {
        verify() {}
        check() {
            if (flying) "The pixies easily evade your pinching fingers. ";
            else if (capturedButAwake) "If you try to extract them from the scarf,
                they\'ll only fly away again. ";
            else "It\'s not clear why you\'re trying to do that. ";
        }
    }
    dobjFor(CatchWith) {
        verify() { logical; }
        check() {
            if (gIobj && (gIobj != scarf))
                "Not a bad idea, but you need to try it with something else. Something
                that resembles a net, perhaps. ";
        }
        action() {
            doInstead(Catch, self);
        }
    }
    dobjFor(Spray) {
        preCond = [objVisible]
        verify() {}
        check() {
            "If you want to spray something onto the pixies, you\'ll need to be specific. ";
        }
    }
    iobjFor(SprayOn) asIobjFor(PourOnto)
    iobjFor(PourOnto) {
        verify() {}
        check() {
            if (gDobj != eauBottle) "You splash a little on the pixies, but it has
                no visible effect. ";
            else if (!capturedButAwake && !groggy)
                "You aim a dash of the fragrance at the pixies, but you miss. Dang!
                Trying again, you miss again. They're just too quick. Maybe you could
                trap them somehow, though. ";
            else if (groggy) "You've already sedated them. ";
        }
        // If I've worked it out correctly, at this point we know they're captured
        // but awake:
        action() {
            capturedButAwake = nil;
            groggy = true;
            "You pour a generous dose of l'eau d'ennui on the pixies. Their frenetic
            actions subside. They begin yawning. ";
        }
    }
;

// We need to put the business of making the pixies visible in the joe object,
// because we don't know where he may be. This could happen while he's following you
// up to the office, for instance.
// But it's more twisty than that. You can give him shoes and get rid of the pixies
// in either order. That is, his ActorStates should probably include HangingOut,
// NoPixies, HasShoes, and ShoesNoPixies. In HasShoes he'll follow you to the office
// and to the thing with the button, after which he will wander off. If he's in NoPixies
// but lacks shoes, he'll be ever so grateful but he won't go anywhere with you because
// he's barefoot.
// So the action of getting rid of the pixies can occur either in HangingOut or in
// HasShoes. It will handle turning off the pixies AgendaItem.

joe: Actor 'homeless man; scruffy large scruffy-looking; joe himself; him' @footOfPole
    "{The joe} is rather large, with a short scruffy beard.
    <<registerSeen>><<joePixelated.showReactions()>>Untrimmed greasy
    hair straggles out from beneath a battered top hat. The hat gives him rather the air
    of a ringmaster several years after the circus left town without him. His arms and legs
    jut out at odd angles<<if (pairOfShoes.wornBy != joe)>>,
    and there are large holes in his socks, a fact that is easily observed, as he isn't wearing any
    shoes<<else>>. He's wearing a handsome pair of boots<<end>>. "
    globalParamName = 'joe'
    friendly = nil
    pixelated = true
    refuseCommandMsg = '<q>What? Did you say somethin\', lady? I guess I missed it.</q> '
    aName = (proper ? 'Joe' : 'the scruffy man')
    giveName {
        proper = true;
        replaceVocab('Joe; scruffy large scruffy-looking homeless; man himself; him');
    }
    registerSeen { seenAlready = true; }
    seenAlready = nil
    theName {
        // Not sure why I had this.
//        if (!seenAlready) {
//            registerSeen;
//            return 'a homeless man';
//        }
        if (!proper) return 'the homeless man';
        return 'Joe';
    }
    // The pixieCatcher method does all of the handling of the action of catching
    // the pixies, including situations where they don't end up being captured.
    // This method will be invoked whenever you're in the room with Joe and the cigar
    // is lit, but that can happen in either order.
    // So we need a daemon to invoke it.
    pixieCatchDaemonID = nil
    pixieCatchDaemonStart() {
        if (pixieCatchDaemonID == nil) {
            pixieCatchDaemonID = new Daemon (self, &pixieCatcher, 1);
        }
    }
    pixieCatchDaemonStop() {
        if (pixieCatchDaemonID != nil)
        {
            pixieCatchDaemonID.removeEvent();
            pixieCatchDaemonID = nil;
        }
    }
    pixieCatcher() {
        if (!pixelated) {
            pixieCatchDaemonStop();
            return;
        }
        if (getOutermostRoom() != gPlayerChar.getOutermostRoom())
            return;
        // So now we know you're in the same room with Joe, and that he's
        // still pixelated. However, the cigar may just have gone out without
        // the pixies having been dealt with, so we may need to schlep the
        // invisible pixies back in.
        if ((!cigar.isDirectlyIn(gPlayerChar)) || (!cigar.isLit)) {
            if (pixelated) {
                flyingPixies.moveInto(nil);
                invisiblePixies.moveInto(joe);
            }
            return;
        }
        // We're part of the way to the solution:
        if ((flyingPixies.isIn(scarf)) && (flyingPixies.capturedButAwake))
        {
            "The pixies are <<one of>>struggling to escape from<<or>>thrashing in
            the folds of<<or>>grappling determinedly with the fabric of<<at random>> the
            scarf! <<one of>>Better do
            something quickly!<<or>>You'll need a plan for what to do next.<<or>>Before
            long they're likely to escape.<<at random>> ";
            return;
        }
        // In this branch, you've dosed them with the eau, so we're done.
        if ((flyingPixies.isIn(scarf)) && (flyingPixies.groggy)) {
            flyingPixies.moveInto(nil);
            scarf.containsPixies = nil;
            joe.pixelated = nil;
            pixiesAch.awardPointsOnce();
            if (joe.curState == joeHasShoes)
                joe.setState(joeShoesNoPixies);
            else
                joe.setState(joeNoPixies);
            "The groggy pixies fall limply out of the scarf and fly away in broad
            drunken loops. {The joe} watches them go and applauds energetically.
            <q>You done it! I didn't think you could do it! No more pixies! Anything
            I can do in return, just let me know.</q> ";
            
            return;
        }
        // Now we know the cigar is lit, so there's smoke, 
        // and we know the pixies are still on the loose ...
        // no, we don't know that! They may be in the scarf!
        // In either event, we're
        // ready for some possible action. The flyingPixies will be
        // embedded in joe, because it's vaguely possible that all this
        // will be happening while he's following you.
        invisiblePixies.moveInto(nil);
        flyingPixies.moveInto(joe);
        // This line is going to print every turn:
        "In the cloud of smoke from the cigar, you're able to see some
        pixies <<if (flyingPixies.isIn(scarf))>>struggling against the folds
        of the scarf<<else>>flying around, darting at Joe's face<<end>>. ";
        // We'll leave the next bits of action to the tender mercies of
        // the scarf and the eau.
    }
    followCount = 0
    cantFollowCount = 0
    noticedNotFollowing = nil
    followDaemon {
        // If this is the start of a new trek, we'll reset things, to be tidy:
        if (followCount == 0) {
            cantFollowCount = 0;
            noticedNotFollowing = nil;
        }
        local rm = gPlayerChar.getOutermostRoom();
        local goodRoom = joeFollowingRegion.roomList.indexOf(rm);
        // Joe isn't where you are, so he's going to try to follow you:
        if (joe.getOutermostRoom() != rm) {
            // In this branch, you've left his willing-to-follow region,
            // so he's going to wait for you.
            if (!goodRoom) {
                if (!noticedNotFollowing) {
                    "You notice that {the joe} is no longer following you. ";
                    noticedNotFollowing = true;
                }
                // else noticedNotFollowing = nil;
                cantFollowCount++;
                // He's not going to wait around all afternoon. Also, we don't want him
                // popping up, absurdly, if you go down to the crypt in the elevator and
                // later show up in his following region by some other route:
                if (cantFollowCount > 3) {
                    joe.moveInto(footOfPole);
                    joe.stopFollowing();
                    noticedNotFollowing = true;
                    followCount = 0;
                    cantFollowCount = 0;
                    joe.stopFollowing();
                }
                return;
            }
            // In this branch, he's actively following you:
            else {
                // We want you to be able to futz with things in the office
                // for as long as you like, so we won't count the passing turns
                // in this location:
                if (getOutermostRoom != worldHeadquarters) followCount++;
                // However, he will eventually get bored and wander off:
                if (followCount > 22) {
                    joe.moveInto(footOfPole);
                    joe.stopFollowing();
                    followCount = 0;
                    cantFollowCount = 0;
                    "<q>Hey, see ya around,</q> {the joe} says. <q>If you need me,
                    you know where to find me.</q> He wanders off. ";
                    return;
                }
                noticedNotFollowing = nil;
                cantFollowCount = 0;
                joe.moveInto(rm);
                if (joe.getOutermostRoom() != hotRags)
                    "{The joe}
                    <<one of>>follows<<or>>saunters after<<or>>shuffles along behind<<or>>dawdles
                    but soon catches up with<<or>>strides boldly ahead and then turns to wait for<<or>>walks
                    beside<<at random>> you. ";
            }
        }
        // In this branch, Joe is following you and you have entered the boutique. The first
        // possibility is that he has already helped you find Bianca, but he's still willing to
        // follow you because you haven't yet solved the world headquarters button puzzle.
        
        // The second possibility is that you've already been to world headquarters and dealt
        // with that, but you haven't yet tried (perversely) to remove the dress from the room,
        // so inanimateBianca is still here.
        
        // The third possibility is that we're ready to rock and roll.
        
        // There's another nasty possibility, which is that you have asked him to follow
        // you here but he doesn't have the dowsing rod.
        if (rm == hotRags) {
            if (bianca.discovered) {
                "{The joe} looks perplexed. <q>We already been here, right? You want me
                to lead you through there again? I know I saw a door in that little room.
                You don't need me. There's another way in. Just go around the
                other way.</q> He shakes his head in
                a sad, dismissive way and saunters out. ";
                joe.moveInto(footOfPole);
                joe.stopFollowing();
                joe.followCount = 0;
                cantFollowCount = 0;
            }
            else if (inanimateBianca.getOutermostRoom() == hotRags) {
                "<q>These the mannequins you was tellin' me about?</q> {The joe}
                gazes around the room appreciatively. <q>They're pretty things,
                aren't they? But I don't see you need any help findin' 'em, they're
                all right here. If you got any trickier things you need help with,
                you know where to find me.</q> He tips his hat in a friendly way
                and saunters out. ";
                joe.moveInto(footOfPole);
                joe.stopFollowing();
                joe.followCount = 0;
                cantFollowCount = 0;
            }
            // In this branch, Bianca has fled, but Joe doesn't have the dowsing rod:
            else if (!dowsingRod.isIn(joe)) {
                "<q>Hey, this is a nice store,</q> {the joe} says, gazing around.
                <q>I never been in here before. Not sure I can help you with any
                lady-shoppin', though. I'm not too up to date on fashions, I guess
                you could say. Now, if you wanted me to find somethin\' in here, that\'d
                be a different kettle of fish. You ever heard of dowsing? Used to be I was pretty
                handy at findin\' stuff if I had a dowsing rod. If you find somethin\'
                like that I could use, you know where to find me. In the meantime,
                all I can say is good luck.</q> He favors you with a quick
                grin and wanders out. ";
                joe.moveInto(footOfPole);
                joe.stopFollowing();
                joe.followCount = 0;
                cantFollowCount = 0;
            }
            // We'll let Bianca handle her own discovery scene.
            else { bianca.discovery; }
        }
    }
    cannotTakeFromActorMsg(obj) {
        return 'The <<obj.name>>? The poor guy has little enough as it is. ';
    }
;

+ topHat: Decoration 'top hat'
    "The top hat has seen better days. "
;

+ socks: Decoration 'socks; ; stockings holes sock hole'
    "The socks have seen better days. "
    plural = true
;

+ feet: Component 'feet; ; foot toe toes'
    "His toes are sticking out through gaping holes in his socks. "
;

+ beard: Component 'scruffy beard;; hair'
    "His beard is scruffy. "
    vocabLikelihood = -5
    dobjFor(Feel) asDobjFor(Pull)
    dobjFor(Pull) {
        verify() {}
        check() { "That wouldn't be polite! "; }
    }
;

+ invisiblePixies: Unthing 'pixies; mischievous little; fairy fairies pixie pixy sprite sprites; them'
    'You don\'t see any pixies. '
;

+ joePixelated: AgendaItem
    initiallyActive = true
    // we'll make it ready when you look at him or speak to him
    isReady = (joe.getOutermostRoom() == gPlayerChar.getOutermostRoom())
    // showReactions is invoked when you first examine joe. He won't exhibit
    // this behavior until you've done that. Possibly this is a bad idea,
    // so for now let's skip it.
    showReactions() {
        // if (!isDone)
        // isReady = true;
    }
    // it will become done when the pixies are dealt with
    isDone = nil
    invokeItem() {
        if (((joe.curState == joeHasShoes) || (joe.curState == joeHangingOut))
        && (!scarf.containsPixies)) {
            local r = rand(5);
            if ((r == 2) && (flyingPixies.flying)) {
                "{The joe} <<one of>>waves his hands furiously around his face<<or>>flails
                around at random<<or>>blinks, shakes his head rapidly,<<at random>> and says, 
                <q><<one of>>Damn these pixies!<<or>>Here they come again!<<or>>Git away
                from me! Not you, lady, talkin' about the darn pixies.<<at random>> They're
                always flyin' at me. A guy can't hardly<<tPixies.makeFamiliar()>> 
                think!</q> ";
            } 
        }
    }
;

// These conversation topics are built in such a way as to respond appropriately, no matter
// what state joe is in, so they can safely be embedded directly in the joe object.

+ DefaultAskTellTopic
    "<q>Can\'t say I know much about stuff like that,</q> he replies. "
;

+ AskTopic @junctionBox
    "<q>Oh, that thing.</q> He lifts his shoulders in a shrug. <q>I heard some guys talkin'
    about it when they were out here. There's some secret code. You gotta put the switches in
    some order or other. I seen the guy write the code down on a piece of paper, but then he
    put it in his pocket and walked off with it, so don't ask me what the code
    was.<<if (junctionBox.isOpen)>> Also, there's
    supposed to be a cable, plugs into those two jacks there.<<end>></q> "
;

+ AskTellTopic @tParade
    "<q>Yeah, I heard about that,</q> he says. <q>I guess it\'s a big deal.</q> "
;

+ AskTopic, StopEventList @joe
    [
        { : "<q>How are you doing?</q> you ask.<<joePixelated.showReactions()>>
        <.p><q>Me? Just fine. Couldn't be better,</q> he says in a booming voice that
        attempts good cheer without quite achieving it. <q>Don't know if we've 
            met. I'm Joe<<joe.giveName>>, that's my name, me.</q> He thumps his
            chest proudly with a meaty hand. " },
        
        '<q>Do you live around here, Joe?</q> you ask.<.p>
        <q>Oh, yeah,</q> he replies. <q>Just about exactly around here, you know?
        Or mostly. Here, there, around, you know how it is.</q> ',
        
        { : "<q>Are you doing all right, Joe?</q> you say.<.p>
        <q>Me? Oh, sure. Fantastic. <<if joe.pixelated>>All except for the---</q> He waves his hands around
        his face. <q>All except for the damn pixies always comin\' at me. Flyin\' right in
        my face all the time. You see me
        twitchin\' and dancin\' around, the pixies is why. Wish I could get rid of &rsquo;em!
        Maybe you can\'t see &rsquo;em, but I sure can! <<end>><<if (pairOfShoes.wornBy != joe)>> 
        I wouldn\'t mind havin\' a righteous pair of shoes, though. Some bastard stole my shoes
        while I was asleep. Took &rsquo;em right offa my feet. They was good shoes, too.<<end>></q> ',
        
        'Joe has already told you pretty much everything there is to know about himself: He\'s
            homeless<<if ((pairOfShoes.wornBy != joe) && joe.pixelated)>>, shoeless, and
            bedeviled by invisible pixies<<else if (joe.pixelated)>> and bedeviled by
            invisible pixies<<else if (pairOfShoes.wornBy != joe)>> and shoeless<<end>>/ " }
    ]
;
+ AskTopic [invisiblePixies, flyingPixies]
    "<q><<if (!joe.pixelated)>>Thanks for gettin' rid of 'em, lady! If you need a favor, just
    ask.<<else>>Them? Always comin' at me. They're a damn nuisance, is what they are. Wish
    somebody'd get rid of &rsquo;em for me.<<end>></q> "
;

// This topic should fire in response to either "mannequin" or "mannequins" if
// the player has for some weird reason entered the boutique but not yet tried
// to leave with the dress.

+ AskTellTopic [mannequins, inanimateBianca]
    "<q>Have you seen the mannequins in the boutique downstairs?</q> you ask.
    <.p><q>Not hardly,</q> he replies. <q>They wouldn't much want me in there,
    I expect.</q> "
;

+ DefaultGiveShowTopic, ShuffledEventList
    [
        '<q>That\'s nice of you,</q> he says, <q>but I got no use for that
        kind of stuff.</q> ',
        
        '<q>I got holes in my pockets,</q> he says. <q>Most anything you give
        me, it\'d just fall out.</q> ',
        
        'He gazes at you in perplexity. <q>What would I do with somethin\'
        like that?</q> '
    ]
;

+ GiveShowTopic @wineBox
    "<q>I never touch the stuff,</q> he says. <q>It messes with your head. Anyway,
    that looks like cheap rotgut.</q> "
;

+ GiveShowTopic @pairOfShoes
    topicResponse () {
        pairOfShoes.moveInto(joe);
        pairOfShoes.wornBy = joe;
        joe.friendly = true;
        shoesAch.awardPointsOnce();
        // This should be the only two possible states we're moving out of:
        if (joe.curState == joeHangingOut)
            joe.setState(joeHasShoes);
        else if (joe.curState == joeNoPixies)
            joe.setState(joeShoesNoPixies);
        "<q>Here,</q> you say, holding out the shoes. <q>I thought maybe you could
        use these.</q>
        <.p><q>Could I? No kidding!</q> He snatches the shoes, jams his feet into them,
        and executes a clumsy dance step. <q>Not a bad fit, hey? Thanks! If you want me
        to return the favor, just say the word. I'll follow you anywhere. Or help
        you find somethin'. I'm good at findin' stuff.</q> ";
    }
;

// You can give the dowsing rod to joe at any time. He will accept it and give you a hint
// about what it's for, but he won't actually be willing to do anything with it until
// you've solved his problems.

+ GiveTopic @dowsingRod
    topicResponse() {
        dowsingRod.moveInto(joe);
        "<q>Here,</q> you say, holding out the forked stick. <q>I thought this might be
        useful.</q><.p>
        <q>Oh, yeah,</q> he says. <q>You know what this is, right? It's a dowsing rod.
        I've used one lots of times. It's for finding things. If there's ever anything
        you want found, just let me know, okay?</q> ";
        if ((joe.curState != joeHasShoes) && (joe.curState != joeShoesNoPixies))
        {
            "He frowns down at his feet. <q>Only thing is, I got no shoes. I can't
            just go wanderin' around without I got some shoes, you know?</q> ";
        }
    }
;

// Question: Is it useful to have Joe be able to 'press the other button'? Probably
// not. This CommandTopic and the next one will only operate in response to the
// command to press one
// of the buttons, so it can safely be embedded directly in joe. In addition, joe
// will only obey if flogg and grabby are gone.

+ CommandTopic @Hold
    topicResponse () {
        if ((gDobj == floggButton) || (gDobj == grabbyButton)) {
            if (flogg.getOutermostRoom() == worldHeadquarters) {
                "{The joe} looks at you uncertainly. <q>Do you think they'd like
                that?</q> ";
                exit;
            }
            if (gDobj.beingHeld) {
                if (gDobj.beingHeldBy == joe) "He's already holding that button. ";
                else "He can't very well push that button while you're already holding it. ";
                exit;
            }
            else if (((gDobj == floggButton) && (grabbyButton.beingHeldBy == joe)) ||
                     ((gDobj == grabbyButton) && (floggButton.beingHeldBy == joe))) {
                "<q>I can't do that,</q> he complains. <q>I can't hold both buttons at
                once --- my arms ain't that long!</q> ";
            }
            else {
                gDobj.beingHeld = true;
                gDobj.beingHeldBy = joe;
                
                safeDoor.buttonPressed(gDobj);
                if (gDobj.releaseFuseID == nil) {
                    local releaseFuseID = new Fuse (gDobj, &releaseFuse, 5);
                    gDobj.releaseFuseID = releaseFuseID;
                    "He obligingly sticks his hand under the desk. <q>Got it,</q> he says. ";
                }
                else "Error --- this should never print. ";
            }
        }
        else {
            "<q>Now you're gettin' me all confused,</q> he complains. ";
            exit;
        }
        
    }
    matchDobj = [floggButton, grabbyButton]
;

+ CommandTopic [Push, Feel]
    topicResponse () {
        if ((gDobj == floggButton) || (gDobj == grabbyButton)) {
            if (flogg.getOutermostRoom() == worldHeadquarters) {
                "{The joe} looks at you uncertainly. <q>Do you think they'd like
                that?</q> ";
                exit;
            }
            if (gDobj.beingHeld) {
                if (gDobj.beingHeldBy == joe) "He's already holding that button. ";
                else "He can't very well push that button while you're already holding it. ";
                exit;
            }
            if (((gDobj == floggButton) && (grabbyButton.beingHeldBy == joe)) ||
                     ((gDobj == grabbyButton) && (floggButton.beingHeldBy == joe))) {
                "<q>I can't do that,</q> he complains. <q>My arms ain't that long!</q> ";
                exit;
            }
            "He obligingly presses the button for just a moment. (If you need him to hold
            the button, tell him that.) ";
            safeDoor.buttonPressed(gDobj);
        }
        else {
            "<q>Now you're gettin' me all confused,</q> he complains. ";
            exit;
        }
    }
    matchDobj = [floggButton, grabbyButton]
;

+ CommandTopic @Follow
    topicResponse () {
        // This will later need to be amended, because joeNoPixies will be a
        // separate state:
        if ((getActor.curState != joeHasShoes) && (getActor.curState != joeShoesNoPixies)) {
            "In response to your request, {the joe} looks at you
            in a puzzled way. <q>I don't think I can do that,</q> he says. <q>I
            got no shoes, see? Can't go wanderin' around just anywhere without shoes.</q> ";
            return;
        }
        "<q>I'd like you to come with me,</q> you tell him. <q>I think you may be able to
        help give me a hand with something.</q>
        <.p><q>Sure thing,</q> he replies promptly. <q>You betcha. Lead on.</q> ";
        joe.startFollowing();
        // joeFollowAgenda.isReady = true;
    }
    matchDobj = (gPlayerChar)
;
+ CommandTopic @StopFollowing
    topicResponse() {
        if (joe.fDaemon == nil) {
            "He looks at you in confusion. <q>What?</q> ";
        }
        // else, yes, he's following you:
        else {
            joe.stopFollowing();
            "<q>Okay, then. If you need me, you know where to find me.</q> 
            <<if(joe.getOutermostRoom() != footOfPole)>>He saunters off.<<end>> ";
            joe.moveInto(footOfPole);
        }
    }
    matchDobj = (gPlayerChar)
;

+ CommandTopic [Open, Lift, Take]
    topicResponse() {
        // The player could try to get Joe to take the lid either while it's on the
        // tank or while it's lying on the floor:
        if (!tankLid.isDirectlyIn(lowerConcourseSouth))
            "<q>If I did that, the octopus would get out,</q> {the joe} says. <q>I been
            arrested before for mischief. It's no fun, bein' arrested.</q> ";
        else "<q>I got no use for a big sheet of plywood,</q> he says. ";
    }
    matchDobj = tankLid
;

+ AskTellTopic @tPromDress
        "<q>I came here this afternoon to get a dress for my daughter for the senior prom,</q>
        you tell him.<.p>
        <q>That's nice.</q> He nods absently. The topic of prom dresses seems not to interest him. "
;

//------------------------------------------------------------
// ActorStates for Joe the helpful homeless guy
//------------------------------------------------------------

// Initially, he's just hanging out. He won't do anything to help you.

+ joeHangingOut: ActorState
    specialDesc = "<<if (!joe.proper)>>A rather large, scruffy-looking man<<else>>Joe<<end>>
        is standing here. "
    stateDesc = "He's <<randomDesc.doScript()>>. "
    randomDesc: ShuffledEventList {
        [
            'scratching his beard',
            'waving a hand around his face in a twitchy way, as if trying to shoo a fly',
            'lifting <<if (joe.friendly)>>one foot to admire his new boots<<else>>a bare foot to stare at it<<end>>',
            'fiddling with his hat',
            'yawning',
            'blinking amiably at you',
            'staring at you in a vague, uncertain way',
            'rubbing his gut',
            'examining a fingernail with great absorption'
        ]
    }
    isInitState = true
;

++ HelloTopic, StopEventList
    [
        '<q>Uhh, hi,</q> you say.
            <.p><q>Hello, there,</q> he responds in a loud and somewhat stilted tone.
            <q>I thought everybody went off to that parade. Guess not.</q>',
        
        '<q>Hello again,</q> you say. <q>I\'m back.</q>
        <.p><q>Well, that\'s okay,</q> he replies. <q>Always glad to see you. Sorry
        I forgot your name.</q> '
    ]
;

// ----------------------------------------------------------------------------
// Now joe HasShoes, but he's not free of the pixies:

+ joeHasShoes: ActorState
    specialDesc = "{The joe} is <<one of>>scratching his beard<<or>>adjusting his hat to
        a jaunty angle<<or>>picking at a tooth with one finger<<or>>gazing around in a
        distracted way<<or>>smiling and nodding. Now and then he casts an
        admiring glance down at his new shoes<<at random>>. "
    stateDesc = "He's <<joeHangingOut.randomDesc.doScript()>>. "
;

// This topic should become available only after the dress is snatched. But
// it's possible that Joe won't have shoes or that he may still have pixies.

++ AskTellTopic @bianca
    "You tell {the joe} about the mannequin stealing the prom dress and
    running off into the dressing rooms. He shakes his head sorrowfully.
    <q>Wish I could help with that,</q> he says. <q>But I get so damn
    distracted all the time with these pixies always comin' at me.</q> "
;

// -----------------------------------------------------------------------------
// Here, you've dealt with the pixies, but joe is still barefoot:

+ joeNoPixies: ActorState
    specialDesc = "{The joe} is grinning at you. Now and then he frowns and wiggles his
        bare toes. "
    stateDesc = "He's <<randomDesc.doScript()>>. "
    // Here we have a slightly different set of stateDesc options, because
    // he won't be waving his hands in front of his face.
    randomDesc: ShuffledEventList {
        [
            'scratching his beard',
            'fiddling with his hat',
            'yawning',
            'blinking amiably at you',
            'gazing at you in a vague, uncertain way',
            'rubbing his gut',
            'examining a fingernail with great absorption'
        ]
    }
;

++ AskTellTopic @bianca
    "You tell {the joe} about the mannequin stealing the prom dress and
    running off into the dressing rooms. He shakes his head sorrowfully.
    <q>Wish I could help with that,</q> he says. <q>I might even be able
    to help you find her. But---</q> He points down at his bare feet. <q>I
    ain't in much shape to go nowhere. Sorry.</q> "
;

+ joeShoesNoPixies: ActorState
    specialDesc = "{The joe} is grinning at you. <q><<one of>>There's one of my favorite
        people<<or>>Thanks again, lady<<or>>How ya doin\'<<or>>I hope I can
        do you a favor sometime<<at random>>,</q> he says. "
    stateDesc = "He's <<joeNoPixies.randomDesc.doScript()>>. "
;

// This topic should become available only after the dress is snatched, I hope. But
// it's entirely possible that Joe won't have shoes or that he may still have pixies.

++ AskTellTopic, StopEventList @bianca
    [
        { : "<q>I've got a problem,</q> you confess to {the joe}. <q>You said you're good
            at finding things, right? There's this mannequin in the boutique --- I know you'll
            think this is crazy, but I swear it's true! She grabbed a dress away from me
            and ran off into the dressing rooms! A mannequin!</q>
            <.p><q>That don't surprise me,</q> he says. <q>I seen plenty of weird stuff
            happening around here.</q>
            <.p><q>So do you think you could maybe help me find her? She's somewhere in
            the dressing rooms....</q>
            <.p><<if (!dowsingRod.isIn(joe))>><q>Sure, I guess,</q> he says. <q>But you know
            what I need? I need a dowsing rod. One of those forked sticks. Without that,
            I'm no better than a blind guy.</q><<else>><q>Sure thing! Lead on!</q> " },
        { : "You and {the joe} chat for a minute about the fugitive
            mannequin. <<if (!dowsingRod.isIn(joe))>>He reminds you that he needs a dowsing
            rod in order to be able to help you find her.<<else>>He's ready to help you.<<end>> "
        }
    ]
;
