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

// To do: Prevent the helmet from being filled in the @restroom.

dirtPatch: Fixture 'patch of bare dirt; square spaded; earth soil' @grassySlope
    "The square patch of dirt looks as if it has recently been spaded up; the dirt is not
    packed down. "
    dobjFor(PutIn) {
        // No, you don't need to pick up the dirt before trying to put it somewhere:
        preCond = [objNotHeld]
        verify() {}
        check() {
            // You can't put dirt anywhere but the helmet or the pot:
            if (gIobj && (gIobj != largePot) && (gIobj != armyHelmet)) "That's not a good place to put any dirt. ";
            // But maybe one of those is the gIobj:
            else if (gIobj && (gIobj == armyHelmet) && (!armyHelmet.isDirectlyIn(gPlayerChar)))
                "You need to be holding the helmet in order to do that. ";
            else if (!armyHelmet.isDirectlyIn(gPlayerChar))
                "You'll need something to scoop up the dirt
                with.<<if (armyHelmet.isIn(me))>> You could probably use the helmet.<<end>> ";
            else if (largePot.getOutermostRoom() != grassySlope)
                "Carrying a helmet full of dirt would be a bit silly. If you want to put some dirt
                into another container, that container will need to be nearby. ";
        }
        action() {
            // Here, the pot takes care of filling itself, because we know the player is holding the helmet.
            // Ah, but we don't know the pot is in scope, do we?
            largePot.fillWithDirt();
            exit;
        }
    }
    dobjFor(Dig) asDobjFor(Scoop)
    dobjFor(DigWith) asDobjFor(ScoopWith)
    dobjFor(LookIn) {
        verify() {}
        check() { "There seems to be nothing of much interest in the dirt, other than the dirt itself. "; }
    }
    dobjFor(Take) {
        verify() { logical; }
        check() { "With your bare hands? Using a shovel (or something like a shovel) would be a good idea. "; }
    }
    dobjFor(Scoop) {
        verify() {}
        check() { if (!armyHelmet.isIn(me)) "You'll need something of suitable capacity if you want to scoop
            up any dirt. "; }
        action() {
            doInstead (ScoopWith, self, armyHelmet);
        }
    }
    // ScoopWith incorporates 'scoop into', which is a slightly different action:
    dobjFor(ScoopWith) {
        verify() {}
        check() {
            if ((gIobj != armyHelmet) && (gIobj != largePot)) "Scooping up dirt with {the iobj} would not be practical. ";
        }
    }
    iobjFor(FillWith) {
        verify() {}
        check() {
            if ((gDobj != largePot) && (gDobj != armyHelmet)) "You'd only get your hands all mucky. ";
        }
    }
;

// Among the things we need to worry about is whether something else is already in the pot
// when you start filling it with dirt.

largePot: Container, PDportable 'large ceramic pot; handsome' @goingToPot
    "The handsome ceramic pot is more than a foot in diameter. <<if (fullOfDirt)>>At the moment it's
    filled with dirt. <<end>>"
    initSpecialDesc = "Sitting next to the wheel is a large ceramic pot. "
    bulk = 120
    stillNeeded() { return true; }
    ditchedOnce = nil
    // The fillWithDirt method will handle all of the actual functionality of filling the pot
    // with dirt, no matter where it's called from:
    fillWithDirt() {
        if (fullOfDirt) {
            "You've already filled the pot with nice fresh dirt. ";
            return;
        }
        // Probably not necessary to check this, but let's be tidy:
        if (gPlayerChar.getOutermostRoom() != grassySlope) {
            "That's not something you can do here. ";
            return;
        }
        if(armyHelmet.contents.car() != nil) {
            "You'll need to empty the helmet before you try using it to scoop up
            dirt. ";
            return;
        }
        if (isIn(gPlayerChar)) {
            "A pot full of dirt is going to be too heavy for you to carry.
            Better set it down first --- and maybe, just as a suggestion, not on the ground?
            If it's on the ground, after you fill it with dirt it's going to be too heavy to lift. ";
            return;
        }
        if (!armyHelmet.isDirectlyIn(gPlayerChar)) {
            "You'll need something to scoop up the dirt
            with.<<if (armyHelmet.isIn(me))>> You could probably use the helmet.<<end>> ";
            return;
        }
        if (contents.car() != nil) {
            local len = contents.length();
            for (local i = 1; i <= len; i++) {
                contents.car.moveInto(getOutermostRoom);
            }
            "Not wanting to cover anything useful with dirt, you start by emptying
            out the pot. ";
        }
        fullOfDirt = true;
        replaceVocab ('large ceramic pot; filled full (of) (with) handsome; dirt soil');
        "You scoop half a dozen helmetsful of dirt into the pot. Soon it's jolly full
        of dirt. ";
    }
    fullOfDirt = nil
    fullOfMiraklGro = nil
    iobjFor(PourOnto) asIobjFor(PourInto)
    iobjFor(PourInto) {
        verify() {}
        check() {
            if (gDobj != miraklGro) "It's not clear that pouring {the dobj} into the pot
                would be useful. ";
            else if (!fullOfDirt) "Not a bad idea, but maybe you should fill the pot with
                dirt first. ";
            else if (!vineSeeds.isDirectlyIn(self)) "The dirt isn't going to grow by
                itself. Maybe if you put some seeds into the pot.... ";
            else if (!gPlayerChar.canSee(mummy)) {
                "The vines sprout with alarming vigor, quickly snaking around your
                limbs and tightening their grip! You\'re unable to move! Three hours
                later you\'re freed by a nice man with a weed whacker, but by
                then ... no, let\'s say that never happened. Let\'s say you think better
                of it and don\'t sploosh the MiraklGro on the pot quite yet.
                When you find a good reason to do it, maybe the result will be more to
                your liking. ";
            }
            else if (mummy.curState == mummyBound)
                "You've already grown the vines. There's no need for an extra dose of MiraklGro. ";
        }
        // We only reach this point in the pour into action if (a) the gDobj is miraklGro,
        // (b) the pot is full of dirt, (c) the vine seeds are in the pot, (d) the player
        // can see the mummy, and (e) the mummy is not yet tangled in the vines:
        action() {
            fullOfMiraklGro = true;
            mummyAch.awardPointsOnce();
            "You slosh the MiraklGro onto the dirt.
            Within seconds a fantastic tangle of lively vines sprouts from the pot and
            entangles the mummy! The mummy writhes in vain; it's far more securely bound
            now than by mere bandages. ";
            mummy.bindWithVines();
            vineSeeds.moveInto(nil);
            bigVines.moveInto(ancientCrypt);
        }
    }
    // We'll let you haul the pot out of the wagon ONLY if the mummy is safely bound:
    dobjFor(Take) {
        check() { if (((fullOfDirt) && (mummy.curState != mummyBound)))
                "The dirt-filled pot proves to be much too heavy to lift. ";
            }
        action() {
            if (fullOfDirt && (mummy.curState == mummyBound)) {
                moveInto(ancientCrypt);
                "With some difficulty, you're able to wrestle the pot out of the wagon and set it on the
                floor. ";
            }
            else inherited;
        }
    }
    // The grammar for ScoopWith also allows 'scoop into':
    iobjFor(ScoopWith) {
        verify() {}
        check() {
            if (gDobj != dirtPatch) "Scooping {the dobj} into the pot probably wouldn\'t accomplish
                anything useful. ";
            else if (!armyHelmet.isDirectlyIn(me)) "The pot is too large and cumbersome to be used
                as a scoop.<<if (armyHelmet.isIn(me))>> You could probably use the helmet.<<end>> ";
        }
        actioni() {
            fillWithDirt();
        }
    }
    dobjFor(FillWith) {
        verify() {}
        check() {
            if (fullOfDirt) "You've already filled the pot with dirt. ";
            else if (getOutermostRoom() != grassySlope)
                "It's not clear that there's anything around here to fill it with. ";
        }
        action() {
            fillWithDirt();
        }
    }
    dobjFor(Empty) {
        verify () {}
        check () {
            if (fullOfDirt && (getOutermostRoom() != grassySlope))
                "You're planning to just leave a pile of dirt here? ";
            // else we'll let the library get rid of whatever is in it
        }
        // We're going to cheerfully ignore the possibility that the helmet is
        // no longer available:
        action() {
            if (fullOfDirt) {
                fullOfDirt = nil;
                replaceVocab ('large ceramic pot; handsome');
                "You tip the pot on its side and dump the dirt back into the dirt patch. ";
            }
            else inherited;
        }
    }
    // For the seeds:
    iobjFor(PutIn) {
        verify() {}
        check() {
            if ((gDobj == vineSeeds) && (!fullOfDirt))
                "If you're thinking of planting the seeds, maybe you ought to put
                some dirt in the pot first. ";
        }
    }
;

temporaryWater: Thing 'some water'
    "It's water. You know --- wet, transparent, tending to slosh around. "
    dobjFor(Take) {
        check() {
            "In your bare hands? That would be a good trick. ";
        }
    }
    dobjFor(Drink) {
        verify() {}
        action() {
            "You take a tiny sip of the water. It tastes ... fishy. ";
        }
    }
    dobjFor(Pour) {
        verify() {}
        check() {
            "You'll need to be specific about what you want to pour the water onto or into. ";
        }
    }
    // Once the fish and its temporary watery home are in the helmet, we're only going to
    // let you pour it into the birdbath:
    dobjFor(PutIn) {
        preCond = [objVisible]
        action() {
            doInstead(PourInto, self, gIobj);
        }
    }
    dobjFor(PourInto) {
        verify() {}
        check() {
            if (gIobj && ((gIobj == fishTank) || (gIobj == fishTankWater)))
                "You've scooped the water and fish into the helmet, and now you've changed your
                mind? Maybe there's a better container (somewhere in Stufftown) that
                you could usefully pour it into. ";
            else if (gIobj && (gIobj != weirdBirdbath))
                "Pouring the water into {that iobj} would only make a mess. ";
            else if (isIn(weirdBirdbath))
                "The water is already in the weird birdbath. ";
        }
        action() {
            moveInto(weirdBirdbath);
            armyHelmet.containsWater = nil;
            singingFish.moveInto(weirdBirdbath);
            weirdBirdbath.containsWater = true;
            weirdBirdbath.containsFish = true;
            "You empty the helmet into the basin in the weird birdbath. The fish
            darts about, clearly happy with its new home. Poking its fishy snout
            out of the water, it ululates a few bars of an aria from <i>The Magic
            Flute.</i><.p> ";
            archibald.makeHappy();
        }
    }
;

// The helmet starts out hidden, so that it will be revealed when the shelf is searched. Also, it
// will never actually contain any dirt, because the dirt doesn't need to be transported in the helmt
// from one room to another.

armyHelmet: Container, Wearable, PDportable 'old-fashioned army helmet; olive drab bowl-shaped shaped metal; bowl' 
    "The helmet --- most likely a left-over from a World War II movie --- is
    more or less bowl-shaped. It's painted olive drab on the outside. <<if (containsWater)>>The helmet is full
    of water, and there's a fish swimming around in the water.<<end>> "
    initSpecialDesc = "Lying upside down on the shelf is an old-fashioned metal
        army helmet. "
    findHiddenDest = wardrobeShelf
    bulk = 25
    isPortable = true
    // Do we even want you to be able to put water in the damn thing without the fish?
    // No, let's not allow that. But we may as well keep the property, so as to be able
    // to test it.
    // We will need to get rid of the water/fish before using the helmet as a shovel.
    containsWater = nil
    containsFish = nil
    notifyInsert (obj) {
        if ((containsWater) && (obj != singingFish)) {
            "Better not. {The subj obj} would only get wet. ";
            exit;
        }
    }
    dobjFor(Wear) {
        verify() {}
        check() {
            if (containsWater) "You'd look awfully silly with
                water running down your face! ";
            else "It's not your style. ";
        }
    }
    // Yes, random things can be carried around in the helmet, so we need this action:
    dobjFor(Empty) {
        verify() {}
        check() {
            if (containsWater)
                "Pouring the poor fish out onto the ground would be worse than cruel! ";
        }
    } 
    // the fillWithWater method can be called from 'scoop water with helmet,' and that
    // command does no error-checking, so we'll do it here:
    fillWithWater() {
        if (containsWater) "You've already filled the helmet with water. ";
        else if (me.getOutermostRoom() != petShop)
            "There's no water nearby that you need to fill the helmet with. ";
        else if (singingFish.isIn(weirdBirdbath))
            "You've already done the bit with transporting the water and the fish to the
            fish's new home. ";
        else {
            containsWater = true;
            temporaryWater.moveInto(self);
            singingFish.moveInto(self);
            "You fill the helmet with water, in the process gathering up the fish. Careful, now --- don't spill it! ";
        }
    }
    iobjFor(CatchWith) {
        preCond = [objHeld]
        verify() {}
        check() {
            if (gDobj != singingFish)
                "There seems to be no way to catch {the dobj} in the helmet. ";
            else if (singingFish.isIn(self))
                "You've already got the fish in the helmet. ";
        }
        // We'll let the fish handle the action.
    }
    // Apparently we're not going to let you put anything at all in the helmet -- how rude:
    iobjFor(PutIn) {
        preCond = [objHeld]
        verify() {}
        check() {
            if ((gDobj != fishTankWater) && (gDobj != dirtPatch) && (gDobj != singingFish))
                "The helmet looks like a good container for carrying things, but
                really there are only a few things that you can't manage to carry
                using some other container, and most of them wouldn't fit in the helmet either. ";
        }
        // We'll let the gDobj handle the action.
    }
    // in response to 'pour helmet into birdbath, we'll let the temporary water handle it:
    dobjFor(PourInto) {
        verify() {}
        check() {
            if (!containsWater) "There's nothing in the helmet that's suitable for pouring. ";
        }
        action() {
            doInstead(PourInto, temporaryWater, gIobj);
        }
    }
    // FillWith routes here, after insuring that the thing we're filling with is
    // either the fishTankWater or the dirtPatch. But the Fill action might be tried
    // anywhere.
    dobjFor(Fill) {
        verify() {}
        check() {
            if (containsWater) "The helmet is already full of water. ";
            else if (contents.car() != nil) "Before you fill the helmet, you should probably
                remove what's in it. ";
            else if ((gPlayerChar.getOutermostRoom() == restroom) ||
                (gPlayerChar.getOutermostRoom() == besideTheCreek))
                "Filling the helmet with water is a reasonable idea, but water by itself
                is maybe not quite the right thing. Have you seen any water anywhere that
                might be more useful? ";
            else if ((gPlayerChar.getOutermostRoom() != petShop) &&
                (gPlayerChar.getOutermostRoom() != grassySlope))
                "There seems to be nothing here that's suitable to fill the helmet with. ";
            // Has the player already moved the fish but now is trying again, for no
            // reason at all?
            else if ((gPlayerChar.getOutermostRoom() == petShop) && (singingFish.isIn(weirdBirdbath)))
                "There's no longer anything in the fish tank that's worth putting in the helmet. ";
        }
        action() {
            // We may be in the pet shop and about to fill the helmet from the fish tank,
            // or in grassySlope and about to fill the helmet with dirt. At this point all
            // we know for sure is that we're in one of those places or the other, that
            // there's nothing in the helmet, and that the fish has not yet been transported
            // to the birdbath.
            if (gPlayerChar.getOutermostRoom() == petShop) {
                singingFish.moveInto (armyHelmet);
                temporaryWater.moveInto (armyHelmet);
                containsWater = true;
                "You scoop up a helmet full of water, in the process capturing the singing fish. ";
            }
            // Otherwise, we must be in the grassySlope:
            else {
                local rm = getOutermostRoom();
                if (largePot.getOutermostRoom() == rm)
                    largePot.fillWithDirt();
                else {
                    "Carrying a helmet full of dirt around with you is not likely to
                    be useful. If you want to use the helmet as a shovel to fill something
                    else, that would be a different matter --- but you'll need to bring that
                    other container out here in order to do it. ";
                }
            }
        }
    }
    dobjFor(FillWith) {
        verify() {}
        check() {
            if (getOutermostRoom() == petShop) {
                if (gIobj && (gIobj != fishTankWater))
                    "Filling the helmet with {the iobj} --- what an odd idea! ";
                else if (containsWater) "The helmet is already full of water and fish. ";
            }
            else if (getOutermostRoom() == grassySlope) {
                if (gIobj && (gIobj != dirtPatch))
                    "Filling the helmet with {the iobj} --- what an odd idea! ";
            }
        }
        action() {
            doInstead(Fill, self);
        }
    }iobjFor(DigWith) asIobjFor(ScoopWith)
    iobjFor(ScoopWith) {
        preCond = [objHeld]
        verify() {}
        check() {
            if ((gDobj != fishTankWater) && (gDobj != dirtPatch) && (gDobj != singingFish))
                "Scooping up {the dobj} with the helmet is probably a bad idea. ";
            else if ((me.getOutermostRoom() == grassySlope) && (!largePot.isIn(grassySlope)))
                "Carrying around a helmet full of dirt is not likely to be useful. If
                you want to fill some other container with dirt, you'll need to bring the
                other container here. ";
        }
        action() {
            if (getOutermostRoom() == grassySlope)
                largePot.fillWithDirt();
            // else we're clearly in the petShop, but there could be quite a variety of
            // issues to be dealt with:
            else if (gDobj == fishTankWater) fillWithWater();
            // if gDobj == singingFish, the fish should handle the action. 
        }
    }
    // If there's water (and fish) in the helmet, we certainly don't want you letting
    // go of it!
    dobjFor(ThrowAt) {
        check() {
            if (containsWater) "The water would spill out all over everywhere. ";
        }
    }
    dobjFor(Throw) asDobjFor(Drop)
    dobjFor(Drop) {
        check() {
            if (containsWater) "If you set the helmet down, it will tip over, and the fish
                will spill out. On the whole, this is probably a bad idea. ";
        }
    }
    dobjFor(PutOn) {
        check() {
            if (containsWater) "If you do that, the helmet will tip over and the singing fish
                will spill out. Better not. ";
        }
    }
    dobjFor(PutIn) {
        check() {
            if (containsWater) "If you do that, the helmet will tip over and the singing fish
                will spill out. Better not. ";
        }
    }
;

longTankTable: Surface, Fixture 'long table' @petShop
    "The long table provides support for the fish tank. "
;

+ fishTank: Container, Fixture '(fish) tank'
    "The fish tank is larger than a breadbox, but smaller than a coffin. In it is a quantity
    of water. <<if (singingFish.isIn(fishTank))>>An unusual-looking fish is swimming in lazy
    figure-eights in the tank. "
    contentsListedInLook = nil
    iobjFor(PutIn) {
        verify() {}
        check() {
            if (gDobj == singingFish) {
                if (singingFish.isIn(fishTank)) "The fish is already in the tank. ";
                else "Maybe the fish would be happier somewhere else (or would make somebody
                    else happier). ";
            }
            else if (gDobj != armyHelmet) "{The dobj} would only get wet. ";
            // but what if the fish isn't in the tank anymore?
            else if (!singingFish.isIn(fishTank))
                "You\'ve already scooped up the fish. ";
        }
        action() {
            doInstead(PutIn, singingFish, armyHelmet);
        }
    }
    contentsListedInExamine = nil
    iobjFor(PourInto) {
        verify() {}
        check() {
            if ((gDobj != armyHelmet) && (gDobj != temporaryWater))
                "The fish tank is already pretty full. ";
        }
    }
;

++ fishTankWater: Fixture 'some water'
    "There is nothing remarkable about the water. It's wet. "
    dobjFor(PutIn) {
        preCond = [objVisible]
        verify() {}
        check() {
            if (gIobj && (gIobj != armyHelmet))
                "What an odd idea! ";
        }
        action() {
            doInstead(FillWith, armyHelmet, self);
        }
    }
    iobjFor(FillWith) {
        verify() {}
        check() {
            if (gDobj != armyHelmet) "That would be messy. ";
        }
    }
;

// The fish will always be either in the fishTank, the helmet, or the weird birdbath:

++ singingFish: Thing 'singing fish'
    "The fish is nearly a foot long, and has iridescent scales. It also has
    a curious and remarkable habit of virtuoso vocalization.
    Apparently this is a singing fish. From time to time it pokes
    its mouth out of the water and trills out a phrase from a well-known pop song or classical
    composition. <<startSinging()>>"
    listenDesc = "The fish pokes its head out of the water, clears its throat, and <<one of>>yodels<<or>>belts
        out<<or>>hums<<or>>intones<<at random>> a few bars of <<one of>><q>House of the Rising
        Sun.</q><<or>>Mozart's 40th Symphony.<<or>><q>We've Only Just Begun.</q><<or>>Handel's <q>Hallelujah
        Chorus.</q><<or>><q>Rudolph, the Red-Nosed Reindeer.</q><<or>>the theme music
        from <i>Doctor Who</i>.<<or>><q>Bewitched, Bothered, and Bewildered.</q><<or>><q>76
        Trombones.</q><<or>><q>Roxanne.</q><<or>><q>Whip It.</q><<at random>> "
    
    // These two preConds are so the parser won't try to take the fish out of the helmet
    // if you show it or give it to Archibald:
    dobjFor(ShowTo) {
        preCond = [objVisible]
    }
    dobjFor(GiveTo) {
        preCond = [objVisible]
    }
    dobjFor(Take) {
        check() {
            if (isIn(armyHelmet)) "Take it out of the helmet? Bad idea. ";
            // else it's either in the tank or in the birdbath, in which case Take is not
            // the right command:
            else
            "You've heard the phrase <q>fish out of water</q>? Picking up the fish would kill it,
            and that would be a damn shame.
            <<if (isIn(fishTank))>>If you want to transport it anywhere, you'll need a container
            that can hold water.<<end>> ";
        }
    }
    dobjFor(ScoopWith) asDobjFor(CatchWith)
    dobjFor(CatchWith) {
        verify() {}
        check() {
            if (gIobj && (gIobj != armyHelmet))
                "You can't catch the fish in {the iobj}! ";
            else if (isIn(armyHelmet))
                "The fish is already in the helmet. ";
        }
        action() {
            doInstead(PutIn, self, armyHelmet);
        }
    }
    dobjFor(PourInto) {
        verify() {}
        check() {
            if (isIn(weirdBirdbath)) "The fish seems to be quite happy where it is. ";
            else if (isIn(fishTank)) "You\'d have to up-end the whole tank. Surely there\'s a
                better way. ";
            else if (gIobj && ((gIobj == fishTank) || (gIobj == fishTankWater)))
                "You've scooped the water and fish into the helmet, and now you've changed your
                mind? Maybe there's a better container (somewhere in Stufftown) that
                you could usefully pour it into. ";
            else if (gIobj && (gIobj != weirdBirdbath))
                "Pouring the fish and the water into {that iobj} would only make a mess. ";
        }
        action() {
            doInstead(PutIn, self, weirdBirdbath);
        }
    }
    // This is an allowed action; it relies on the helmet having an iobjFor(PutIn)
    // that allows it, and indeed the helmet does have that code:
    dobjFor(PutIn) {
        // because we want to be able to put it in the helmet without an implicit take:
        preCond = [objVisible]
        // But is the fish in the tank, or is it in the water? This matters!
        verify() {
            if (gIobj && isIn(gIobj)) illogicalAlready ( 'The fish is already in {the iobj}. ' );
        }
        check() {
            if ((gIobj != armyHelmet) && (gIobj != weirdBirdbath) && (gIobj != fishTank))
                "The fish wouldn't be happy there. ";
            else if (isIn(weirdBirdbath)) "The fish seems happy enough in its new home,
                and {the archibald} is certainly happy to have it there. On balance, there seems to be no
                further reason to traipse about Stufftown burdened with a singing fish. ";
        }
        action() {
            // This is giving a bogus message that refers somehow to the putter!
            if (gIobj == armyHelmet) {
                armyHelmet.containsWater = true;
                temporaryWater.moveInto(armyHelmet);
                moveInto(armyHelmet);
                "You scoop the fish <<startSinging()>>and a quantity of water into the helmet. ";
            }
            else if (gIobj == weirdBirdbath) {
                armyHelmet.containsWater = nil;
                temporaryWater.moveInto(weirdBirdbath);
                moveInto(weirdBirdbath);
                weirdBirdbath.containsWater = true;
                weirdBirdbath.containsFish = true;
                "You pour the fish and the helmet full of water into
                the weird birdbath. ";
                archibald.makeHappy();
            }
            // I don't think this can be reached, but let's leave the code alone for now.
            else if (gIobj == fishTank) {
                armyHelmet.containsWater = nil;
                temporaryWater.moveInto(nil);
                moveInto(fishTank);
                "Changing your mind, you pour the fish back into the tank. ";
            }
        }
    }
    singDaemonID = nil
    startSinging() {
        if (!singDaemonID) singDaemonID = new SenseDaemon(self, &sing, 1, &canHear);
    }
    stopSinging() {
        if (singDaemonID) {
            singDaemon.removeEvent();
            singDaemonID = nil;
        }
    }
    archieHasCommented = nil
    sing() {
        if (rand(4) == 3) {
            listenDesc;
            if ((gPlayerChar.getOutermostRoom() == artGallery) && (archibald.curState == archibaldSad)
                && (!archieHasCommented)) {
                "{The archibald} perks up his ears for a moment. <q>What was that? I thought for a
                moment I heard ... never mind.</q> ";
                archieHasCommented = true;
            }
        }
    }
;

+ Component, Decoration 'iridescent scales; ; rainbow'
    "The scales are all the colors of the rainbow. "
;

weirdBirdbath: Container, Fixture
    'weird birdbath; surreal surrealist surrealistic angular deformed chrome chromium plastic cupped bird; patch patches bath twigs straw basin'
    @artGallery
    "The birdbath (if that's what it is) is about three feet high and has a cupped
    basin in the top. The basin, an inverted dome that dips down into the upper surface
    of the birdbath, is about the right size to hold a basketball or a
    watermelon. Leaving aside the smooth interior of the basin, the construction is
    angular and asymmetrical --- deliberately deformed,
    if you like. Bits of beige plastic intersect small blocks of metal, and several
    darker patches that might be twigs or straw protrude from the lower
    surface.<<if(singingFish.isIn(self))>> The birdbath is filled with water, and a fish
    is swimming around happily in it.<<end>> "
    bulkCapacity = 40
    containsWater = nil
    containsFish = nil
    iobjFor(PutIn) {
        verify() {}
        check() {
            if ((gDobj != singingFish) && (gDobj != temporaryWater))
                "That might well detract from the aesthetic qualities (whatever they are)
                of the birdbath. ";
        }
    }
    iobjFor(PourInto) {
        verify() {}
        check() {
            if ((gDobj != armyHelmet) && (gDobj != temporaryWater) && (gDobj != singingFish))
                "That might well detract from the aesthetic qualities (whatever they are)
                of the birdbath. ";
        }
        // The action is handled by the armyHelmet and temporaryWater
    }
;
