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

//other rooms:
//   
//    belowBrokenStairs
//    mineshaft
//    ancientCrypt
//    litteredHallway
//    elevator
//    shippingDepartment
//    batCave
//    grotto



//----------------------------------------------------------------------------
// belowBrokenStairs:
//----------------------------------------------------------------------------

belowBrokenStairs: Room 'Below the Broken Stairway'
    "This narrow, damp, and poorly lit passage extends away to the north. A staircase
    descends from above, but it's shattered just below the level of the ceiling, leaving
    no way to ascend. To the south is a rusted metal door. "
    
    north = mineshaft
    south = rustyDoorInterior
    up = "The stairway is broken. There's no way up from here. "
;

+ Decoration 'broken stairway; shattered hanging; ceiling staircase stair stairs'
    "Apparently the stairway used to descend to this level, but it's just hanging there now. "
;

+ rustyDoorInterior: Door 'rusty door; rusted corroded; corrosion iron metal'
    "The door seems to be made of iron, or possibly of some metal that is more
    prone to corrosion, as it's badly rusted. Along one side are some hinges. "
    isLocked = true
    lockability = indirectLockable
    isAttackable = true
    cannotAttackWithMsg = 'You flail away at the door, but you don\'t seem
        to have accomplished anything. '
    otherSide = rustyDoorExterior
    dobjFor(AttackWith) {
        verify() { logical; }
        check() {
            if (isOpen) "You've already taken care of opening the rusty door. ";
            else if (gIobj && (gIobj != mallet))
                "You flail away at the door with {the iobj}, but you
                don't seem to have accomplished anything. ";
        }
        action() {
            "Hitting the door with the mallet proves not to work. Next, you
            have a go at the hinges. ";
            doInstead (AttackWith, hinges, gIobj);
        }
    }
    dobjFor(Close) {
        verify() {}
        check() {
            if (!isOpen) "The door isn\'t open. ";
            else "Maybe just leave it open. Letting a little fresh air inside
                couldn\'t hurt. ";
        }
    }
;

++ hinges: Component 'hinges; bent; hinge; them'
    "The hinges appear to be bent out of alignment. "
    dobjFor(Straighten) {
        verify() {}
        check() {
            "There's no obvious way to do that. Maybe just hit them with
            something. ";
        }
    }
    dobjFor(Attack) {
        verify() { logical; }
        check() {
            if (!mallet.isDirectlyIn(me)) "You don't seem to have
                a suitable implement. ";
            else if (rustyDoorInterior.isOpen) "No need to do that --- the
                door is already open. ";
        }
        action() {
            doInstead (AttackWith, hinges, mallet);
        }
    }
    dobjFor(AttackWith) {
        verify() { logical; }
        check() {
            if (gIobj && (gIobj != mallet))
                "{The subj iobj} {have} no effect on the hinges. ";
            else if (rustyDoorInterior.isOpen) "No need to do that --- the
                door is already open. ";
        }
        action() {
            rustyDoorInterior.makeLocked(nil);
            rustyDoorInterior.makeOpen(true);
            "By banging on the hinges with the mallet, you're able to loosen
            the hinges. The door swings open, letting in some sunlight! ";
        } 
    }
;

//----------------------------------------------------------------------------
// mineshaft:
//----------------------------------------------------------------------------

mineshaft: Room 'Mineshaft'
    "This rough-hewn passage appears to have been carved out of solid rock. It continues
    to the north and south. The only sign of civilization is a sliding door set in the west
    wall. Next to the door is affixed a brass plate. "
    
    south = belowBrokenStairs
    north = ancientCrypt
    west = cryptElevatorDoor
    in asExit(west)
;

+ Decoration 'solid rock; rough hewn rough-hewn; wall walls'
    "The walls are of rough-hewn rock. "
;

+ cryptElevatorDoor: ElevatorDoorExterior '+'
    otherSide = cryptElevatorDoor
;

+ cryptBrassPlate: Fixture 'brass plate'
    "The brass plate is mounted beside the sliding door. In its center is
    a round black button. "
;

++ cryptSummonButton: SummonButton '+'
    myNumber = 0
;

//----------------------------------------------------------------------------
// ancientCrypt
//----------------------------------------------------------------------------

ancientCrypt: Room 'An Ancient Crypt'
    "<<one of>>As unlikely as it must seem, Stufftown appears to have been constructed atop an
    ancient Egyptian burial chamber. Or perhaps, on mature reflection, it's not all that unlikely.
    <<or>><<stopping>>Though not large, this chamber affects a certain austere dignity. The hieroglyphs
    carved in the walls are vaguely Egyptian. Or possibly they were carved recently by someone
    who had looked briefly at photos of Egyptian hieroglyphs in a high school history
    book and didn't remember them very well.
    
    <.p><<one of>>Faintly, the reedy music of an Egyptian <i>arghul</i> is being piped in --- or, no, surely that's
    just your imagination. Less conjecturally, s<<or>>S<<stopping>>ituated in the center of the room
    is a large and imposing stone sarcophagus.
    
    <.p>Open passageways lead north and south. "
    
    south = mineshaft
    north = batCave
;

+ ceilingTunnel: Door, Decoration 'high tunnel; ; gap'
    "Faintly visible is a gap in the ceiling. "
    notImportantMsg = 'Nope. It\'s hopelessly out of reach. Not even if you had
        a stepladder. '
;
    
+ wallGlyphs: Decoration 'hieroglyphs; indecipherable carved Egyptian; symbols glyphs carving carvings; them'
    "The hieroglyphs on the walls add a certain mysterious or mystical atmosphere to the scene. A
    great many symbols are visible, but most of them are so indistinct or so poorly rendered that 
    deciphering them is likely to prove hopeless. "
;

+ sarcophagus: Fixture 'sarcophagus; large imposing (stone) coffin'
    "The stone sarcophagus is about three feet high, three feet wide, and seven
    feet long. It's made of sand-colored stone, and the stone is somewhat chipped and
    scarred, suggesting that the sarcophagus may be of great
    age. <<if (sarcophagusLid.location == remapOn)>>A thick
    lid of the same stone lies atop the sarcophagus. <<end>>"
    remapIn: SubComponent {
        isOpen = nil
    }
    remapOn: SubComponent {}
    dobjFor(AttackWith) {
        verify() {}
        action() {
            doInstead (AttackWith, sarcophagusLid, gIobj);
        }
    }
    dobjFor(Open) {
        verify() { logical; }
        check() {
            if (sarcophagusLid.location == remapOn)
                "You give the lid a shove, but it's much too heavy for you to
                make it budge. ";
            else "The sarcophagus has already been opened. ";
        }
//            "With a dry scraping noise, the impossibly heavy lid of the sarcophagus slides sideways.
//            It falls to the floor in a cloud of dust with an echoing <i>boom</i>, revealing the interior
//            of the sarcophagus. ";
//        }
    }
;

++ sarcophagusLid: Heavy '(sarcophagus) lid; heavy (stone)'
    "The lid is obviously carved of stone. "
    subLocation = &remapOn
    cannotTakeMsg = 'No amount of pushing or pulling will budge the heavy stone lid. '
    hammeredCount = 0
    dobjFor(Open) {
        verify() {}
        action() { doInstead(Open, sarcophagus); }
    }
    dobjFor(Attack) {
        verify() {}
        check() {
            if (sarcophagus.remapIn.isOpen) "There would seem to be no reason to do that. ";
            else "You flail at the lid with your bare hands, thereby accomplishing nothing.
            Possibly you need to hit it with something harder. ";
        }
    }
    dobjFor(AttackWith) {
        verify() { logical; }
        check() {
            if (sarcophagus.remapIn.isOpen) "There would seem to be no reason to do that. ";
            else if (gIobj && (gIobj != mallet)) "You flail away at the lid, but your
                effort has no obvious effect. ";
        }
        action () { 
            hammeredCount++;
            if (hammeredCount == 1)
                "You strike the lid of the sarcophagus firmly several times with the mallet. 
                As the echoes subside, you hear a faint sifting or stirring sound within
                the sarcophagus. ";
            else if (hammeredCount == 2)
                "Again you pound on the lid with the mallet. As you watch, the lid shifts 
                just an inch or two to one side, and a low
                moan escapes from it. ";
            else if (hammeredCount == 3) {
                moveInto(ancientCrypt);
                sarcophagus.remapIn.makeOpen(true);
                sarcoAch.awardPointsOnce();
                "At your final blow the lid is thrust aside from underneath and
                falls to the floor with a resounding crash. A figure wrapped in cloth
                (a mummy, one would be forced to conclude) lifts itself slightly, turns
                its head as if to look around, and then falls back into the sarcophagus. ";
            }
        }
    }
;

//--------------------------------------------------------------
// The mummy
//--------------------------------------------------------------

#ifdef __DEBUG

Test 'mummy'
    [
        'gonear sarcophagus', 'purloin mallet', 'hit lid with mallet', 'g', 'g',
        'take toe', 'purloin saw', 'saw toe', 'purloin large pot', 'purloin vine seeds',
        'purloin wagon', 'drop wagon', 'put pot in wagon', 'potfill',
        'put seeds in pot', 'purloin bottle of miraklgro', 'pour miraklgro into pot',
        'x mummy', 'take toe', 'saw toe', 'i', 'pull wagon s', 'take wagon', 'pull wagon s'
    ]
;

DefineIAction(PotFill)
    execAction(cmd) {
        largePot.fullOfDirt = true;
        miraklGro.isFixed = nil;
        "Magically, the pot is full of dirt. ";
    }
;
VerbRule(PotFill)
    'potfill'
    : VerbProduction
    action = PotFill
    verbPhrase = 'fill/filling the pot'
    missingQ = 'what do you want to fill'
;
#endif

mummy: Actor 'mummy' 
    "The mummy is wrapped from head to toe in gray bandages. "
    // specialDesc = "Lying on its back in the sarcophagus is a mummy. "
    location = sarcophagus.remapIn
    contentsListed = nil
    ownsContents = nil
    bindWithVines() {
        setState(mummyBound);
    }
    freakout() {
        "As you attempt that, the mummy leaps out of the sarcophagus and dashes
        frantically around the crypt, emitting high whining cries of distress. Gradually
        it calms, and at last it settles down in its coffin again. ";
    }
    dobjFor(Unwrap) {
        verify() {}
        check() {
            "You really don't want to know what's under those wrappings. ";
        }
    }
;

+ bandages: Thing 'gray bandages;; bindings wrappings bandage wrapping; them'
    "The bandages bind the mummy from head to toe. Or maybe not quite to toe. On
    closer examination you discover that one of the mummy's toes (or what's left of
    it, that being mostly a toe bone) is protruding from the bandages. "
    // plural = true
;
+ mummyToe: PDportable 'mummy\'s toe; old brown; lump bone'
    "The toe bone is <<if isIn(mummy)>>sticking out of the bandages down there
    at the end of the mummy<<else>>just a brown lump of old bone<<end>>. "
    dobjFor(Take) {
        verify() {}
        check() {
            if (isIn(mummy) && (mummy.curState == mummyEvading)) mummy.freakout();
            else if (isIn(mummy)) "The toe is stubbornly attached to the rest of
                the mummy's foot. You're going to need a tool of some sort to remove it. ";
        }
    }
    dobjFor(CutWith) {
        verify() {}
        check() {
            if (!isIn(mummy)) "You have already severed the toe. ";
            else if (gIobj && (gIobj != shortSaw))
                "{The iobj} {doesn\'t} seem to be a suitable implement. ";
        }
        action() {
            doInstead(Saw, self);
        }
    }
    dobjFor(Saw) {
        verify() {}
        check() {
            if (!shortSaw.isDirectlyIn(gPlayerChar))
                "You don't seem to be carrying a saw. ";
            else if (!isIn(mummy)) "There's no need to keep sawing at it. ";
        }
        action() {
            if (mummy.curState == mummyEvading) mummy.freakout();
            else {
                moveInto(gPlayerChar);
                toeAch.awardPointsOnce();
                "The mummy struggles in vain as you saw off its toe. Got it! ";
            }
        }
    }
;

+ mummyEvading: ActorState
    isInitState = true
    specialDesc = "Sitting up in the sarcophagus and gazing at you in a vague, befuddled
        way is a mummy. "
    stateDesc = "It's sitting in the sarcophagus, its hands folded sedately on its chest. "
;
+ mummyBound: ActorState
    specialDesc = "Deep within a tangle of vines, you can see a mummy. "
    stateDesc = "Firmly bound by the vines, it's unable to move. "
;
//----------------------------------------------------------------------------
// litteredHallway:
//----------------------------------------------------------------------------

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

Doer 'open litteredHallwayExitDoorInterior'
    execAction(c) {
        "You push the exit door open, but as soon as you let go of it, it closes again. ";
    }
    where = litteredHallway
;
    
litteredHallway: Room 'A Littered Hallway'
    "This rather dismal hallway runs north and south, littered for
    most of its length by bits of trash. There's a wide
    door on the east and a sliding elevator door on the west. The hallway
    terminates at the south with a door marked EXIT. "
    
    south = litteredHallwayExitDoorInterior
    west = lowerElevatorDoor
    north = shippingDepartment
    east = shippingDoorInside
;

+ Decoration 'trash; ; bits scraps litter'
    "The trash is no more than you\'d expect in a hallway that hasn\'t
    been swept for a while. "
;

+ litteredHallwayExitDoorInterior: Door 'exit door'
    "It's a solid-looking door with a crash bar. "
    otherSide = litteredHallwayExitDoorExterior
;

+ lowerElevatorDoor: ElevatorDoorExterior '+'
    otherSide = elevatorInteriorDoor
;

+ lowerBrassPlate: Fixture 'brass plate'
    "The brass plate is mounted beside the elevator door. In its center is
    a round black button. "
;

++ lowerSummonButton: SummonButton '+'
    myNumber = 1
;

+ shippingDoorInside: Door 'wide door'
    "It's just an ordinary wide, businesslike door. Mounted next to the door is
    a brass plate, and in the plate is a slot. "
    lockability = indirectLockable
    isLocked = true
    otherSide = shippingDoorOutside
;

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

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

//----------------------------------------------------------------------------
// The elevator:
//----------------------------------------------------------------------------

class SummonButtonPlate: Fixture 'brass plate'
    "The brass plate is mounted on the wall next to the sliding door. In its center
    is a black button. "
;

fakeDoorToNowhere: Door;

class ElevatorDoorExterior: Door 'elevator door; sliding broad gray; edge'
    "The sliding elevator door <<if isOpen>>is visible only as an edge
    along one side of the open doorway<<else>>is broad and gray<<end>>. "
    otherside = elevatorInteriorDoor
    dobjFor(Open) {
        check() {
            if (isOpen) "The door is already open. ";
            else "There's no obvious way to do that ... but you might try pressing
                the button. ";
        }
    }
    dobjFor(Close) {
        check() {
            if (isOpen) "There's no obvious way to do that. ";
            else "The door is already closed. ";
        }
    }
    dobjFor(Lock) asDobjFor(Unlock)
    dobjFor(Unlock) {
        verify() {
            illogical ('There\'s no obvious way to do that. ');
        }
    }
;

class SummonButton: Button, Component 'black button; round'
    "The button is round and black. "
    myNumber = 999
    dobjFor(Push) {
        check() {
            if (elevator.onFloor == myNumber)
                "The elevator is already here, and the door is open. ";
            else if (!junctionBox.isPowered)
                "You press the button, but nothing happens. ";
        }
        action() {
            elevator.summon(myNumber);
        }
    }
;

class ElevateButton: Button, Component 'black button; round'
    "The button is round and black. On it is printed the number <<myNumber>>. "
    myNumber = 999
    dobjFor(Push) {
        check() {
            if (elevator.onFloor == myNumber)
                "You press the button, but nothing happens. ";
            else if (!junctionBox.isPowered)
                "You press the button, but nothing happens. ";
        }
        action() {
            elevator.elevate(myNumber);
        }
    }
;

// These Doers are needed because the elevator plate isFixed, and the library thinks
// pushing it up or down is a travel action.

Doer 'push elevatorInteriorPlate up'
    exec(curCmd) {
        elevatorInteriorPlate.moveUp();
    }
    where = elevator
;
Doer 'push elevatorInteriorPlate down'
    exec(curCmd) {
        elevatorInteriorPlate.moveDown();
    }
    where = elevator
;
            
elevator: Room 'Freight Elevator'
    "The interior of the elevator is large enough for a small freight cart to be rolled
    in and out --- and judging by the black marks on the floor, that has
    happened often. The sliding door is in the east wall. Beside
    the door is a brass plate wherein are situated three black buttons in a vertical row. "
    east = elevatorInteriorDoor
    out asExit(east)
    
    onFloor = 1
    // The elevate method is called by the interior buttons. It will only be called
    // after the button itself has checked to be sure the junction box is powered:
    elevate (floor) {
        "You press the button, and with a slight shudder the elevator door slides closed.
        After a bumpy few moments, it slides open again. ";
        moveTo (floor);
    }
    // The summon method is called by the exterior buttons. It will only be called
    // after the button itself has checked to be sure the junction box is powered:
    summon (floor) {
        "You press the button, and from behind the sliding door you hear a series
        of thumps and groaning noises. In a few moments there's a muffled <i>ding,</i>
        and the door slides open. ";
        moveTo(floor);
    }
    moveTo (floor) {
        closeAllDoors();
        switch (floor) {
        case 3:
            upperElevatorDoor.makeOpen(true);
            elevatorInteriorDoor.otherSide = upperElevatorDoor;
            upperElevatorDoor.otherSide = elevatorInteriorDoor;
            onFloor = 3;
            break;
        case 2:
            mainElevatorDoor.makeOpen(true);
            elevatorInteriorDoor.otherSide = mainElevatorDoor;
            mainElevatorDoor.otherSide = elevatorInteriorDoor;
            onFloor = 2;
            break;
        case 1:
            lowerElevatorDoor.makeOpen(true);
            elevatorInteriorDoor.otherSide = lowerElevatorDoor;
            lowerElevatorDoor.otherSide = elevatorInteriorDoor;
            onFloor = 1;
            break;
        case 0:
            cryptElevatorDoor.makeOpen(true);
            // If the player summoned the elevator down here after arriving
            // through the hatch or the jewel-encrusted grotto, he hasn't
            // solved the puzzle, so no points should be awarded.
            // if (me.getOutermostRoom() == elevator) elevatorAch.awardPointsOnce();
            elevatorInteriorDoor.otherSide = cryptElevatorDoor;
            cryptElevatorDoor.otherSide = elevatorInteriorDoor;
            onFloor = 0;
            break;
        default:
            "Error --- this should never print. ";
            
        }
        elevatorInteriorDoor.makeOpen(true);
    }
    closeAllDoors() {
        upperElevatorDoor.makeOpen(nil);
        upperElevatorDoor.otherSide = upperElevatorDoor;
        mainElevatorDoor.makeOpen(nil);
        mainElevatorDoor.otherSide = mainElevatorDoor;
        lowerElevatorDoor.makeOpen(nil);
        lowerElevatorDoor.otherSide = lowerElevatorDoor;
        cryptElevatorDoor.makeOpen(nil);
        cryptElevatorDoor.otherSide = cryptElevatorDoor;
    }
;

+ Decoration 'black marks; ; mark'
    "Evidently things with rubber tires have been rolled in and out of the elevator. "
    plural = true
;

+ elevatorInteriorDoor: Door 'sliding door; (elevator)'
    "The elevator door is more notional than a reality at the moment, as it has
    slid open. "
    // It's always open:
    isOpen = true
    dobjFor(Close) {
        check() {
            "There's no obvious way to do that. ";
        }
    }
    otherSide = lowerElevatorDoor
;

+ elevatorInteriorPlate: Fixture 'brass plate; ; panel'
    "The brass plate is mounted beside the door, and evidently held in place
    by a pair of screws near the upper corners. Within the plate three recessed buttons are
    mounted in a vertical row. The buttons are black, and are labeled, from top to bottom,
    <<if (position == 1)>>&lsquo;3\', &lsquo;2\', and &lsquo;1\'<<else>>&lsquo;2\',
    &lsquo;1\', and &lsquo;0\'<<end>>. "
    dobjFor(Press) asDobjFor(Push)
    dobjFor(Push) {
        verify() {}
        check() {
            if (!elevatorPlateScrews.unscrewed) "You push on the plate, but
                it doesn't move. ";
        }
        action() {
            if (position == 1) moveDown();
            else moveUp();
        }
    }
    dobjFor(Unscrew) {
        verify() {}
        action() {
            doInstead(Unscrew, elevatorPlateScrews);
        }
    }
    moveUp {
        if (!elevatorPlateScrews.unscrewed) { "You push on the plate, but
                it doesn't move. ";
            exit;
        }
        else if (position == 1) { "You try to push the plate further upward, but
                it doesn't move. ";
            exit;
        }
        else {
            "You push the plate upward. The buttons slide out of view for a moment, and then
            three new buttons (or possibly two of them are the same as before) appear. ";
            position = 1;
            elevate3Button.replaceVocab('black button; three 3 top');
            elevate2Button.replaceVocab('black button; two 2 middle');
            elevate1Button.replaceVocab('black button; one 1 bottom');
        }
    }
    moveDown {
        if (!elevatorPlateScrews.unscrewed) { "You push on the plate, but
                it doesn't move. ";
            exit;
        }
        else if (position == 0) { "You try to push the plate further downward, but
                it doesn't move. ";
            exit;
        }
        else {
            "You push the plate downward. The buttons slide out of view for a moment, and then
            three new buttons (or possibly two of them are the same as before) appear. ";
            elevatorAch.awardPointsOnce();
            position = 0;
            elevate3Button.replaceVocab('black button; two 2 top');
            elevate2Button.replaceVocab('black button; one 1 middle');
            elevate1Button.replaceVocab('black button; zero 0 bottom');
        }
    }
    position = 1
;

++ elevate3Button: ElevateButton '+; three 3 top'
    myNumber { return 2 + elevatorInteriorPlate.position; }
    listOrder = 1
;
++ elevate2Button: ElevateButton '+; two 2 middle'
    myNumber { return 1 + elevatorInteriorPlate.position; }
    listOrder = 2
;
++ elevate1Button: ElevateButton '+; one 1 bottom'
    myNumber { return 0 + elevatorInteriorPlate.position; }
    listOrder = 3
;

++ elevatorPlateScrews: Component 'screws;;; them'
    "The screws are situated near the upper corners of the brass plate. <<if unscrewed>>They
    have been loosened, and protrude a bit from the face of the plate. "
    unscrewed = nil
    dobjFor(Loosen) asDobjFor(Unscrew)
    dobjFor(Unscrew) {
        verify() { logical; }
        check() {
            if (unscrewed) "You have already unscrewed the screws in the brass plate. ";
            else if (!screwdriver.isDirectlyIn(gPlayerChar)) "You'll need a screwdriver if you want to do that. ";
        }
        action() {
            unscrewed = true;
            // elevatorInteriorPlate.moveDown;
            "You loosen the screws somewhat. Even when loosened as far as possible, they won't come
            out, but that may not matter. The plate seems to be looser than before. ";
        }
    }
    dobjFor(LoosenWith) asDobjFor(UnscrewWith)
    dobjFor(UnscrewWith) {
        verify() { logical; }
        check() {
            if (gIobj != screwdriver) "{The subj iobj} {is} not the right tool for manipulating
                the screws. ";
            else if (!screwdriver.isDirectlyIn(gPlayerChar)) "You'll need to be holding the screwdriver
                in order to do that. ";
        }
        action() {
            replaceAction (Unscrew, elevatorPlateScrews);
        }
    }
    dobjFor(Tighten) asDobjFor(Screw)
    dobjFor(Screw) {
        verify() { logical; }
        check() {
            if (!screwdriver.isDirectlyIn(gPlayerChar)) "You'll need a screwdriver if you want to do that. ";
            else if (!unscrewed) "The screws appear already to have been tightened down as far as they'll go. ";
        }
        action() {
            unscrewed = nil;
            if (elevatorInteriorPlate.position == 0) {
                elevatorInteriorPlate.moveUp;
                "You slide the brass plate upward a couple of inches with one hand and tighten the screws
                with the other, using the screwdriver. ";
            }
            else "You tighten the screws. ";
        }
    }
    dobjFor(TightenWith) asDobjFor(ScrewWith)
    dobjFor(ScrewWith) {
        verify() { logical; }
        check() {
            if (gIobj != screwdriver) "{The subj iobj} {is} not the right tool for manipulating
                the screws. ";
            else if (!screwdriver.isDirectlyIn(gPlayerChar)) "You'll need to be holding the screwdriver
                in order to do that. ";
        }
        action() {
            replaceAction (Screw, elevatorPlateScrews);
        }
    }
;

//----------------------------------------------------------------------------
// shippingDepartment
//----------------------------------------------------------------------------

shippingDepartment: Room 'Shipping Department'
    "Business may be brisk at Stufftown, or it may be nearly nonexistent. Either conclusion
    could be drawn from the lack of stock in the shipping department, a cavernous room that
    is all but devoid of merchandise. A large wooden shipping crate sits in the center of
    the bare concrete floor, and a sturdy workbench is built into one wall.
    
    <.p>A wide roll-up door of corrugated metal is in the west wall. Mounted next to it
    is a metal box on which is a red button. A broad but
    more human-sized doorway is to the south. "
    
    proper = true
    south = litteredHallway
    west = rollupDoorInterior
;

+ rollupDoorInterior: Door 'corrugated steel door; roll-up metal large'
    "The door is made of corrugated metal. "
    otherSide = rollupDoorExterior
    lockability = indirectLockable
    dobjFor(Close) {
        verify() {
            if (!isOpen) illogicalNow ('The metal door is already closed. ');
        }
        check() {
            "The cylinder containing the rolled-up door is too high for you to reach,
            but there may be another way to close it. ";
        }
    }
;

+ shippingDeptButtonBox: Fixture 'metal box'
    "The metal box is mounted on the wall next to the roll-up door. Set squarely in the face of the box is
    a large red button. "
;

++ redButton2: Component, Fixture 'large red button'
    "The red button is situated on the front of the metal box. "
    dobjFor(Push) {
        preCond = [touchObj]
        verify() {}
        check() {
            if (!junctionBox.isPowered) "<i>Click.</i> You press the button, but nothing happens. ";
        }
        action() {
            if (!rollupDoorExterior.isOpen) {
                rollupDoorExterior.makeOpen(true);
                "With a loud grumbling, grinding noise, the metal door rolls up, revealing
                the loading dock, and beyond it the sunlit parking lot. ";
            }
            else {
                rollupDoorExterior.makeOpen(nil);
                //rollupDoorExterior.makeLocked(true);
                "With a loud clashing, crashing noise, the metal door rolls down. ";
            }
        }
    }
;

//-------------------------------------
// The shipping crate:
//-------------------------------------

+ shippingCrate: Fixture, Heavy 'shipping crate; wood wooden packing battered; box'
    "The battered wooden shipping crate sits in the middle of the room. It's large enough
    that a forklift might be needed to move it. <<if (!isOpen)>>The top of the crate is not nailed down
    tight; there's a narrow gap between the top and the sides. "
    remapIn: SubComponent, OpenableContainer {
        isOpen = nil
        isLocked = true
        lockability = indirectLockable
        lockedMsg = 'The lid of the crate is nailed down pretty firmly. '
    }
    makeLocked(stat) {
        lockability = notLockable;
        inherited(stat);
    }
    dobjFor(OpenWith) {
        verify() {}
        check() {}
        action() { doInstead (OpenWith, crateLid, gIobj); }
    }
;

++ crateGap: Component, RestrictedContainer 'narrow gap; ; opening'
    "You could probably put something into the gap in order to pry the crate open. "
    allowedContents = [screwdriver]
    iobjFor(PutIn) {
        verify() {}
        check() {
            if (gDobj != screwdriver) "That won\'t fit into the narrow gap. ";
        }
        action() {
            doInstead (OpenWith, crateLid, screwdriver);
        }
    }
;

++ crateLid: Component 'lid;; top'
    "The lid of the crate <<if (shippingCrate.remapIn.isOpen)>>has been pried up, revealing 
    what's inside<<else>>is not nailed down too securely. There's a narrow gap between the lid
    and the sides of the crate<<end>>. "
    dobjFor(OpenWith) {
        verify() { logical; }
        check() {
            if (shippingCrate.remapIn.isOpen) "You have already pried the lid up. ";
            else if (gIobj != screwdriver) "You have a go at prying up the lid, but it
                proves fairly stubborn. Possibly some other implement would produce
                better results. ";
        }
        action() {
            shippingCrate.remapIn.makeLocked(nil);
            shippingCrate.remapIn.makeOpen(true);
            crateGap.moveInto(nil);
            "You slip the blade of the screwdriver through the gap between the lid and the
            crate and push upward. With a shriek of protesting nails,
            the lid allows itself to be pried up. ";
        }
    }
;

++ styrofoamPeanuts: Thing 'styrofoam peanuts;;; them'
    "The styrofoam peanuts fill the crate. "
    subLocation = &remapIn
    iobjFor(PutIn) {
        verify() {}
        check() {}
        action() {
            replaceAction(PutIn, gDobj, shippingCrate);
        }
    }
    dobjFor(Take) {
        check() { "They'd only get all over everywhere. You know how it is with
            styrofoam peanuts and static electricity. Best leave them be. ";
        }
    }
    specialDesc = "A blithering mound of styrofoam peanuts fills the crate. "
    hiddenIn = [nightingale]
    // LookIn handles the Search action, so this code works for both commands:
    dobjFor(LookIn) {
        check() {
            if ((nightingale.moved) || (!nightingale.isIn(nil))) "There seems to be nothing
                else buried among the styrofoam peanuts. ";
        }
        action () {
            nightingale.moveInto(shippingCrate.remapIn);
            "Digging down in the mound of styrofoam peanuts, you find a beautiful
            silver bird. Your encyclopedic knowledge of ornithology enables you identify it as
            a nightingale. ";
        }
    }
;

+ workbench: Fixture, Surface 'workbench; sturdy solid solidly built scarred; bench'
    "The workbench is solidly built, and somewhat scarred from long use. Mounted beneath the
    upper surface is a drawer. "
    remapIn = workbenchDrawer
;

++ workbenchDrawer: Component, OpenableContainer 'drawer; (workbench)'
    "The drawer is mounted beneath the upper surface of the workbench. "
;

//////////////////////////////////////////////////////////////////////////////
// batCave:
//////////////////////////////////////////////////////////////////////////////

batCave: Room 'Bat Cave'
    "It's all but pitch-black in here, but you can catch glimpses of this and
    that because you're carrying a source of light. Glancing upward in horror, you
    find that the ceiling is a veritable carpet
    of bats! Occasionally one drops from its inverted perch and swoops past you.<.p>
    There are exits to the east and south. "
    darkName = 'Stygian Darkness'
    darkDesc = "It's pitch-black in here, but you can hear rustling noises and
        an occasional high-pitched squeak. "
    isLit {
        if (flashlight.isIn(me) && flashlight.isLit) return true;
        else if (candleStub.isIn(me) && candleStub.isLit) return true;
        return nil;
    }
    roomDaemon {
        local number = me.contents.length();
        local something;
        local choice;
        local likelihood = rand(2);
        if ((number > 0) && (likelihood > 0)) {
            if (!isLit) "Something brushes against your face! ";
            else "A large black bat flies straight at you, and its wings brush your face! ";
            choice = rand(number);
            something = me.contents[(choice + 1)];
            if ((!something.isWornBy(me)) && 
                (something != tSamantha) && ((something != flashlight) || (flashlight.isOn))) 
            {
                something.moveInto(batCave);
               "Startled, you drop <<something.theName>>. ";
                if ((something == candleStub) && (candleStub.isLit)) {
                    "The candle flame is snuffed out! ";
                    candleStub.isLit = nil;
                }
            }
        }
    }
    east = grotto
    south = ancientCrypt
;

+ Decoration 'bats; ; bat'
    "<<if (!batCave.isLit)>>You can\'t see any bats, but the rustling and squeaking
    noises are not reassuring.<<else>>The bats are mostly clinging to the ceiling ... except for the ones that are
    flying around.<<end>> "
    plural = true
;

//////////////////////////////////////////////////////////////////////////////
// The grotto:
//////////////////////////////////////////////////////////////////////////////

grotto: Room 'Jewel-Encrusted Grotto'
    "This narrow, crooked space has walls, floor, and ceiling of what appears to be
    raw bedrock. The walls scintillate impressively in the dim light. A stone arch,
    evidently not natural but of human construction, is to the southeast, and the
    grotto curves away into Stygian blackness to the west. "
    canTravelerPass(t) {
        if (t == redWagon) return nil;
        return true;
    }
    explainTravelBarrier(t) {
        "The floor there is much too uneven to roll a wagon across it. ";
    }
    
    southeast = grottoArch
    west = batCave
;

+ grottoArch: OctagonExteriorPassage '+; (southeast)'
;

+ Decoration 'jewel-encrusted walls; raw; bedrock floor ceiling jewel jewelsdiamond diamonds ruby rubies'
    "The walls are studded with jewels. "
    decorationActions = [Examine, Take]
    dobjFor(Take) {
        verify() {}
        check() {
            "You make a mental note to return sometime when you\'re not otherwise occupied to
            load up with diamonds and rubies. At the moment, you have more pressing concerns. ";
        }
    }
;
