#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
#include "AHA.h"

/* 
 *   ViaModerna.t 
 *
 *   This route through Hell is intended to take a wry look at 20th century 
 *   New Testament scholarship.
 */

bunker : Room 'Concrete Bunker' 'the bunker'
    "Little has been left in this concrete bunker apart from a pile of
    sandbags, and a poster fixed to the west wall. The former surround the open
    doorway to the east, which looks out on a shell-torn battlefield. To the
    southwest lies an open archway, while a smart oak door is set incongrously
    in the north wall. "
    enterRoom(traveler)
    {
        if(gRevealed('snap-shut'))
            achievement.awardPointsOnce();
    }
    north = oakDoor
    southwest = hellHall
    east = battlefield
    atmosphereList : ShuffledEventList {
    [
        'From outside you hear the sound of distant shouting. ',
        'A squadron of bombers drones overhead. ',
        'A shell lands near the bunker, causing the whole place to shake, and
        sand to leak from one of the bags. ',
        'There\'s a distant rattle of machine-guns from somewhere outside. ',
        'You hear the sound of a distant explosion. ',
        'A droning V1 rocket cuts out directly overhead -- you hold your breath
        until you hear it explode some distance away. ',
        'There\'s a repeated whine, whistling and roar as bombs fall round the
        bunker, exploding on all sides but mercifully failing to do any
        significant damage. '  
    ]
    }  
    thinkHere = "You think Hell's take on the twentieth century makes up it
        aptness what is lacks in subtlety. "
    achievement: Achievement {+1 "getting<<gRevealed('snap-shut') ? 
          ' past the snappy door ' : ''>> into the bunker" }
    southwestLook = "An open archway lies to the southwest. "
    throwDests = [[northDirection, oakDoor], [southwestDirection, bunkerArch],
        [westDirection, bunkerDoorway]]
;

+ SimpleNoise
    desc() { location.atmosphereList.doScript(); }
;


+ bunkerArch : EntryPortal 'open arch/archway' 'archway'
    "The open archway leads to the southwest. "
    destination = hellHall
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

+ Decoration 'large poster' 'large poster'
    "The poster depicts a grinning Satan pointing to the caption, <q>Welcome to
    the Twentieth Century!</q>"
    initNominalRoomPartLocation = defaultWestWall
    isListedInRoomPart(part) { return isNominallyInRoomPart(part); }
    nothingBehindMsg = 'You can\'t actually see behind the poster, but you\'ve
        no reason to suppose that there\'s anything but the wall there. '
;

++ Component 'grinning pointing cloven devil/satan/horns/tail/hooves' 'Satan'
    "You assume it's meant to be Satan from the horns, tail, and cloven hooves.
    He's shown grinning and pointing to a caption."
    isProperName = true
;

++ Component 'large red caption/letters' 'caption'
    "The caption on the poster reads, in large red letters, <q>Welcome 
    to the Twentieth Century!<q>. " 
;

+ oakDoor : RoomPartItem, Door 'fine polished smart oak door' 'oak door'
    "Such a fine, polished oak door seems more than a little incongruous in
    this setting. It's marked with a sign saying <<marking>>. "
    initNominalRoomPartLocation = defaultNorthWall
    initSpecialDesc = "A fine oak door marked <<marking>>
        is set incongruously in the north wall. "
    marking = '<q>SCHOLARS\' PATH</q>'
;

++ Component '(north) (n) (northern) oak door sign*signs' 'sign'
    "The sign on the oak door reads <q>SCHOLARS' PATH</q>. "
    disambigName = 'oak door sign'
    nothingBehindMsg = &cannotSeeBehindMsg
;

+ bunkerDoorway: RoomPartItem, EntryPortal 
    'open (e) (east) doorway' 'open doorway'
    "The open doorway to the east looks out over a muddy, shell-torn
    battlefield. Above the open doorway is a sign reading <<marking>>. "
    dobjFor(LookThrough)
    { 
        verify() { logicalRank(120, 'open orifice'); }
        action() {
            "Through the open doorway you see a sea of mud that you would
            describe as utterly flattened were it not for the plethora of shell
            craters. About the only thing left standing is a ruined church over
            to the east. ";
        }
    }
    initNominalRoomPartLocation = defaultEastWall
    initSpecialDesc = "In the east wall is an open doorway, marked <<marking>>,
        looking out over a shell-torn battlefield. "
    cannotOpenMsg = 'It\'s already open. '
    cannotCloseMsg = 'There\'s no door to close; it\'s just an open doorway 
        surrounded by sandbags. '
    marking = '<q>WARRIORS\' WAY</q>'
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }  
    disambigName = 'east doorway'
    nothingBehindMsg = &unclearWhatBehindMsg
;

+ Fixture '(e) (east) doorway sign*signs' 'sign'
    "The sign above the doorway sign reads <q>WARRIORS' WAY</q>. "
    disambigName = 'east doorway sign'
    nothingBehindMsg = &cannotSeeBehindMsg
;

+ Distant 'shell-torn sea/mud/battlefield' 'sea of mud'
    "The mud stretches as far as you can see, churned up by shell craters in
    every direction. "
;

+ Distant 'shell plethora/craters' 'shell craters'
    "The shell craters are all over the sea of mud. "
    isPlural = true
;

+ Distant 'ruined church/building/basilica*buildings*churches' 'ruined church'
    "It looks in a pretty bad way; it's still standing, but most of the roof
    seems to be missing and in places it walls are quite badly damaged. So far
    as you can tell it was once a basilica. "
;

+ Decoration 'sand pile/sandbags/bags' 'sandbags'
    "Most of the sandbags are piled round the doorway to the east; a few
    are strewn across the floor. "
    notImportantMsg = 'You might as well leave them alone; they\'re of no
        obvious use to you. '
    isPlural = true
;

+ rifle: Thing 'old-fashioned lee enfield 303 
    lee-enfield bolt/rifle/gun*guns*rifles' 'rifle'
    "You're no expert on armaments, but the gun looks like it could be a
    Lee-Enfield 303. "
    initSpecialDesc = "An old-fashioned rifle leans against the wall. "
    lookInDesc = "A closer examination reveals that it's not loaded. "
    weight = 3
    bulk = 2
    notLoadedMsg = 'The rifle isn\'t loaded. '
    dobjFor(Shoot) asDobjFor(Fire)
    dobjFor(Fire)
    {
        verify() {}
        check() {  failCheck(notLoadedMsg);   }
    }
    iobjFor(ShootWith)
    {
        verify() { }
        check() { failCheck(notLoadedMsg); }
    }
    dobjFor(Load)
    {
        preCond = [objHeld]
        verify() { }
        check() { failCheck('{You/he} {do}n\'t have any ammunition. '); }
    }
    isModern = true
    iobjFor(AttackWith) maybeRemapTo(gVerbName is in ('attack', 'kill'),
                                     ShootWith, DirectObject, self)
//    iobjFor(AttackWith) { verify() { } }
;

//==============================================================================

battlefield : OutdoorRoom 'Battlefield' 'the battlefield'
    "Mud, mud, mud: everywhere is mud, churned up by shells and bombs, and
    scarred with shell craters and barbed wire. The battlefield stretches as
    far as you can see in every direction. Nothing seems to have survived here
    apart from a squat concrete bunker to the west and a ruined church to the
    east; to north and south there is nothing but churned-up mud for as far as
    you can see. "
    west = bunker
    east = ruinNarthex
    in : AskConnector {
        travelAction = EnterAction
        travelObjs = [ruinedChurch, battleBunker]    
        promptMessage = "There are two buildings in sight. "
        isConnectorListed = nil
    }
    north : FakeConnector { "You start off across the seemingly endless sea of
        mud, but turn back when the sound of small arms fire starts to sound
        dangerously close. " }
    south : FakeConnector { "You walk a little way south until you find
        yourself looking out over a wide open plain on which a tank battle is
        in full spate. It's perhaps the strangest tank battle you ever did see,
        since it seems to involve every type of tank from lumbering British
        MkVs through Panzers, Shermans, and T-34s to Abrams, Challengers, and
        T-62s. Bizarre as the sight is, however, you decided that such a battle
        is not something for a lone unprotected New Testament scholar on foot
        to get tangled up in, so you head back towards the relative safety of
        the bunker. "
    }
    
    northwest: NoTravelMessage  
    {
        "You wander off across the battlefield for a short distance, but
        quickly turn back when you find yourself getting too close to the
        fighting. <<rifleCheck()>>";
        rifleCheck()
        {
            if(rifle.isIn(gPlayerChar))
            {
                "Unfortunately, you turn back too late -- one of the
                combatants, seeing you armed, mistakes you for an enemy soldier
                and shoots you. His aim is excellent -- the bullet passes
                straight through your heart. Suddenly you feel an intense
                repetition of the pain that brought you to this netherworld in
                the first place -- and then you know no more, apart, very
                briefly, from a distant voice saying, <q>Pity, I thought we
                might be able to save him.</q>";
                
                finishGameMsg('NOW YOU HAVE REALLY DIED!', 
                              [finishOptionUndo, finishOptionFullScore]);
            }
        }
    }
    
    northeast asExit(northwest)
    southeast asExit(northwest)
    southwest asExit(northwest)
    
    atmosphereList : ShuffledEventList {
    [
        {: "You hear the sound of distant gunfire off to the <<randDir()>>. "},
        
        {: "A plane drones overhead, dropping its load of bombs a little way to
            the <<randDir()>>. "},
        
        'You hear a whoosh and a bang as an artillery shell lands nearby,
        throwing up plumes of mud and adding yet another crater to the
        count. ',
        
        {: "You hear sudden shouts, and turn to see what looks like a
            <<rand('squad', 'platoon', 'company')>> of infantry running up from
            the <<rand('west; but before they reach the bunker', 'north; but as
                they come round the church', 'south; but as they approach',
                       'east; but no sooner do they appear than')>> a barrage
            of artillery fire lands among them, and seconds later there is
            nothing left but a couple more shell craters. " },
        
        {: "A pair of machine-guns chatter away somewhere off to the
            <<randDir()>>. "},
        
        'An artillery shell lands close to the ruined church, damaging its
        stonework still further. ',
        
        {: "A <<rand('blood-red triplane', 'dark green biplane',
                     'single-engined fighter', 'Spitfire', 'Hurricane', 'Zero',
                     'jet fighter')>> dives out of the sky above, strafing the
            ground all around you, forcing you to run and throw yourself to the
            ground. Miraculously, you survive the attack (perhaps because you
            can only die once), and pick yourself up to watch the fighter
            disappear over the horizon. " },
        
        'A large bomber flies overhead, and suddenly releases its load. The
        bombs fall all around the bunker, creating yet more craters, but
        miraculously, both you and the bunker survive. ',
        
        { : "You hear a strange drone overhead, and then recognize it as a V1
            rocket (or <q>doodlebug</q>). Suddenly, the drone cuts out, and in
            the ensuing silence you realize this means the rocket will be
            hitting a target nearby. In the event it explodes a dozen or so
            yards to the <<randDir()>> of the church, shaking the ruined
            building still further. " },
        
        { : "Somewhere off to the <<randDir()>> you hear the wail of a air-raid
            siren. " },
        
        { : "There's a searing flash and a roar like the end of the world, then
            far in the distance a mushroom cloud billows up over the
            <<randDir()>>ern horizon. " }
    ]
        randDir() { 
            return rand('north', 'east', 'south', 'west',
                        'southeast', 'southwest', 'northeast', 'northwest'); 
        }
    }
    thinkHere = "You wonder at the evidence of senseless slaughter and
        destruction, just as you flinch at the din and devastation all around
        you; you conclude that this is not a good place to hang around. "
    eastLook = "Over to the east stands the remains of a heavily-shelled
        basilica. "
    westLook = "To the west lies a squat concrete bunker. "
    defaultLook(dir) { "The muddy battlefield stretches as far as you can
        see to the <<dir.name>>. "; }
;

+ SimpleNoise 'sound/battle' 'sound of battle'
    "The sound of battle is continuous, an endless round of hooting artillery
    shells, droning aircraft and rattling small-arms fire. Every now and again
    you also hear a distant shout about the din. "
;

+ CustomFixture 'dirty sickly unpleasant churned churned-up brown mud' 'mud'
    "The sickly brown mud is everywhere you look, pock-marked by shell-craters
    and churned up by the passage of armies in every direction. "
    lookInDesc = "You can see at a glance that it contains multiple craters and
        fragments of barbed wire. A more thorough search might reveal things
        far more unpleasant, so you refrain from making one. "
    cannotTakeMsg = 'The dirty brown mud looks so obviously unpleasant that
        you\'d much rather leave it alone. '
    isQualifiedName = true
    tasteDesc = "<<cannotEatMsg>>"
    cannotEatMsg = 'Ugh! The very idea makes you feel sick! It looks and smells
        a bit like excrement and would probably taste far worse. No way are you
        going to put any of that mud in your mouth if you can possibly avoid
        it!'
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } } 
    nothingBehindMsg = &unclearWhatBehindMsg
;

++ SimpleOdor 'vile foul decomposing stench/cordite/flesh' 'vile stench'
    "The stench here is positively vile: a mixture of cordite, foul mud, and
    decomposing flesh. "
;

+ Fixture 'shell muddy bloody dirty vile filthy craters/water/blood' 
    'shell craters'
    "A reasonably close inspection shows that many of the craters are filled
    with water; a rather closer inspection shows that in many the water is
    stained with blood; at this point you decide not to press your examination
    any closer. "
    isPlural = true
    cannotDrinkMsg = 'You must be joking!'
    cannotEnterMsg = 'The bloody water in those muddy craters looks vile;
        there\'s no way you want to get into it. '
    feelDesc = "The water in the craters feels unpleasantly cold and gritty. You
        shake it off your hand at once. "
    lookInDesc = "All the craters have filled up with dirty water. " 
    notAContainerMsg = ( gIobj == self ? 'That\'s not a good idea;
        {the dobj/he} would sink under the water and you might never recover
        {it dobj/him} again. ' : nil)
    tasteDesc = "As you might expect, the water in the craters taste vile. You
        spit it out at once. " 
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } } 
    nothingBehindMsg = &unclearWhatBehindMsg
;

+ Decoration 'barbed twisted fragments/bits/pieces/wire' 'pieces of barbed wire'
    "The bits of barbed wire that remain may once have formed part of some
    coherent defensive system, but now they're just fragments, jutting
    incoherently out of the mud or criss-crossing the shell craters without
    rhyme or reason. "
    isPlural = true
    notImportantMsg = 'They\'re no use to you, and you could cut yourself on the
        barbs if you try messing with them. '
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

+ ruinedChurch : Enterable ->ruinNarthex 'ruined stonework building/church/basilica
    *buildings*churches*walls' 'ruined church'
    "Only the shell of the building remains standing, and even that not very
    securely. The roof collapsed long ago, whether from bombs or artillery
    bombardment. From what remains standing, it appears that the church was
    once a basilica. "
    cannotClimbMsg = 'The walls of the church don\'t offer terribly good
        handholds, and, even more to the point, they look quite badly damaged,
        so there\'d be too great a risk that any handholds you found wouldn\'t
        support your weight. '
    nothingBehindMsg = &cannotSeeBehindMsg
;

+ battleBunker : Enterable ->bunker 'squat concrete bunker*buildings*bunkers*walls' 
    'concrete bunker'
    "It's a squat concrete bunker that has somehow survived the shelling and
    bombing despite a number of apparent near misses. The bunker's walls are
    pock-marked with multiple bullet holes, splattered with mud from nearby
    shell craters, and stained with blood from goodness only know where. "
    cannotClimbMsg = 'It looks awkward to climb, and you\'d feel even more
        exposed if you made it to the roof. '
    nothingBehindMsg = &cannotSeeBehindMsg
;

++ Component 'multiple bullet holes' 'bullet holes'
    "They're sprayed randomly over the walls of the bunker, some in groups
    (presumably from machine-gun fire) and others individually (presumably from
    rifle bullets). "
    isPlural = true
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

//==============================================================================

ruinNarthexFloor : Floor 'floor/ground' 'floor'
    "It's covered in dust<<dustyFlagstone.isOpen ? ', and there\'s a large
        square hole in it where a flagstone has been dislodged' :''>>. "
;

ruinNarthexRoof : RoomPart 'patches/roof/ceiling/sky' 'roof'
    "The roof is still there, but it has clearly suffered some damage, since
    you can see patches of sky through it. "
;

ruinNarthexWestWall : defaultWestWall
    desc = "The west wall contains several holes as well as the opening that's
        obviously meant to be there, namely the doorway out (now minus its
        door). "
;

ruinNarthex : Room 'Narthex of Basilica' 'the narthex'
    "<<travelOrigin == gardenCave ? 'The narthex is now looking a bit worse for
        wear. The roof is damaged, and' : 'This seems to be the one part of the
            church that\'s reasonably intact. At least, there\'s still a roof
            of sorts here, although'>> there's the odd hole in the walls, and a
    fine film of dust covers the floor <<travelOrigin == gardenCave ? 'for some
        reason' : 'where plaster has been shaken off by nearby explosions'>>.
    <<dustyFlagstone.isOpen ? 'A flagstone has been dislodged from the floor,
        leaving a square hole. ' : ''>> To both east and west are doorways
    lacking doors; a dusty stone font squats by the western one. "
    west = battlefield
    out asExit(west)
    east = ruinNave
    in asExit(east)
    down = dustyFlagstone
    roomParts = [ruinNarthexFloor, ruinNarthexRoof, ruinNarthexWestWall,
        defaultEastWall, defaultSouthWall, defaultNorthWall]
    thinkHere = "You can't see much to detain you here. "
    vocabWords = 'narthex/church'
    enteringRoom(traveler)
    {
        foreach(local cur in narthex.contents)
            if(isPortable(cur))
            cur.moveInto(self);
    }
    outLook = "The western doorway looks out over a muddy battlefield. "
    throwDests = [[westDirection, rnDoorwayOut], [eastDirection, rnNaveEntrance]]
;

+ Decoration 'white fine film/dust/plaster' 'white dust'
    "There's white dust all over the floor; it looks like plaster shaken off the
    walls and ceiling. "
    isMassNoun = true
    initNominalRoomPartLocation = defaultFloor
    isListedInRoomPart(part) { return isNominallyInRoomPart(part); }
;

+ Decoration 'numerous hole*holes' 'holes'
    "There are numerous holes in the west wall, caused by shell damage, bomb
    damage, and sundry other kinds of battle damage. "
    isPlural = true
;

+ rnDoorwayOut: ExitPortal ->battlefield 
    'west western (w) open doorway*doorways' 'doorway out'
    "The west doorway is little more than a gap in the west wall, looking out
    over the battlefield. "
    dobjFor(LookThrough)
    {
        verify() { logicalRank(120, 'open orifice'); }
        action() {
            "Through the open doorway you see a sea of mud full of shell
            craters.       About the only thing left standing is a concrete
            bunker over to the west. ";
        }     
    }
;

+ Distant 'shell crater-pocked sea/mud/craters/battlefield' 'sea of mud'
    "The sea of crater-pocked mud stretches as far as you can see through the
    doorway, broken only by a lone concrete bunker. "
    dobjFor(Enter) remapTo(West)
;

+ Distant 'lone solitary squat concrete bunker' 'concrete bunker'
    "A solitary squat concrete bunker huddles in the mud in the middle distance
    to the west. Its survival looks something of a miracle. "
;

+ rnNaveEntrance: RoomPartItem, EntryPortal -> ruinNave 
    'east (e) open eastern archway/doorway*doorways' 'archway'
    "The east doorway, devoid of a door, opens into the ruined nave beyond. "
    dobjFor(LookThrough)
    {
        verify() { logicalRank(120, 'open orifice'); }
        action() { rnRuinNave.desc; }      
    }  
    initNominalRoomPartLocation = defaultEastWall
    initSpecialDesc = "An archway leads through the east wall into the nave. "
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

+ rnRuinNave : Enterable, Distant 'ruined nave' 'ruined nave'
    "You can see part of the ruined nave through the open doorway, but you'd
    get a much better view of it from the nave itself, and from what you can
    see it looks perfectly safe to enter. "
    dobjFor(Enter) remapTo(East)
;

+ dustyFlagstone: HiddenDoor ->flagstone 'large square hole*holes' 'square hole'
    "The square hole in the floor is about two foot square. "
;

+ Unthing 'door*doors' 'doors'
    'Presumably there were some doors here once, but there\'s
    not so much as a splinter of them left. '
    isPlural = true
;

+ dislodgedFlagstone: PresentLater, Unthing 'dislodged flagstone' 'flagstone'
    'The dislodged flagstone fell through into the chamber below; it isn\'t
    here. '
;

grammar nounPhrase(doorwayOut) :
    'doorway' 'out'
    : SpecialNounPhraseProd
    getMatchList = [rnDoorwayOut]
    getAdjustedTokens =
    ['doorway', &noun, 'out', &adjective]
;

//==============================================================================
ruinNaveEastWall : defaultEastWall
    desc = "What was the east wall has now largely collapsed into the rubble
        of the apse. "
;

ruinNaveFloor: defaultFloor
    desc = "The floor is covered with rubble. "
;

ruinNave: Room 'Ruined Nave' 'the ruined nave'
    "The nave is open to the sky and full of rubble. The apse (to the east) has
    fallen in completely, and is now quite inaccessible, though fragments of
    what once may have been a mosaic can be seen among the rubble there. Most
    of the columns that once helped support the roof are little more than
    stumps marking the line of the central aisle. An open doorway leads out to
    the west. "
    west = ruinNarthex
    out asExit(west)
    east : NoTravelMessage {
        "That would take you to the apse, but the apse is so badly damaged that
        it's no longer accessible. "     
    }
    
    roomParts = [ruinNaveFloor, defaultSky, ruinNaveEastWall, defaultWestWall,
        defaultNorthWall, defaultSouthWall]
    thinkHere = "You wonder if this shattered church could be considered a
        metaphor for what the twentieth century did to religious belief in much
        of the western world -- or what Hell would like it to have done. "
    throwDests = [[eastDirection, rnExit]]
;

+ rnExit: RoomPartItem, ExitPortal -> ruinNarthex 
    'open doorway*doorways' 'open doorway'
    "The open doorway, at the west of what was once the central aisle, leads
    back to the narthex. "
    dobjFor(LookThrough)
    {
        verify() { logicalRank(120, 'open orifice'); }
        action() {
            "Through the open doorway to the west you can just make out
            part of the narthex. ";
        }
    }  
    initNominalRoomPartLocation = defaultWestWall
    initSpecialDesc = "The doorway back out to the narthex is set in
        the west wall. "
;

+ Distant 'ruined (part)/narthex' 'narthex'
    "You can't really see that much of it from here. "
    dobjFor(Enter) remapTo(West)
;


+ Fixture 'apse' 'apse'
    "The apse has become little more than a pile of rubble, though fragments of
    what may once have been a mosaic can be seen there. "
    cannotEnterMsg = 'The apse has completely collapsed; it\'s simply a pile of
        rubble now, so there\'s no way anyone could enter it. '
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } } 
;

+ Decoration 'central stumps/stubs/columns/aisle/stubs' 'columns'
    "Nearly all the columns lining the central aisle have suffered some damage;
    most of them have been broken off at some point and a few are no more than
    stubs. "
    isPlural = true
;

+ Fixture 'central aisle' 'central aisle'
    "The line of the central aisle, running from the west end of the nave to
    the fallen apse, is more or less discernible from the twin rows of columns
    -- or rather the stumps of columns -- that line it, but the aisle itself is
    mainly buried in rubble. "  
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

+ Readable 'neat neatly neatly-penned penned small scrap/paper/writing*scraps*papers' 
    'small scrap of paper'
    "It's a bit crumpled, but you can make out some writing on it, which looks
    like it was written with an old-fashioned fountain pen. "
    initSpecialDesc = "A small scrap of paper can just be seen poking out of
        the rubble. "
    readDesc = "The neatly-penned handwriting on the note reads\b
        <q><FONT face=TADS-SCRIPT>St Satan's roof appeal...\n
        is now devilishly near completion...\nit's been a hell of a job
        but...</FONT></q>"
    dobjFor(Read)  {  preCond = static inherited + objHeld  } 
    isModern = true
    cannotWriteOnMsg = 'There\'s not much space left to write on it, and it\'s
        so crumpled it hardly seems worth it. '
    futileToKnockOnMsg = 'It rustles a little. '
    weight = 0
;

+ naveRubble: Underside, CustomFixture 'pile/rubble' 'rubble'
    "The rubble, most of which was probably once the ceiling, now lies all over
    the floor. Almost anything could be buried under it. "
    cannotTakeMsg = 'There\'s really not much you can usefully do with a load
        of rubble. '
    isMassNoun = true
    dobjFor(LookIn) asDobjFor(LookUnder)  
    dobjFor(Search) asDobjFor(LookIn)
    dobjFor(LookUnder) 
    {
        action()
        {
            foreach(local obj in nave.contents)
            {
                if(isPortable(obj))
                    obj.moveIntoForTravel(self);
            }
            foreach(local obj in apse.contents)
            {
                if(isPortable(obj))
                    obj.moveIntoForTravel(self);
            }
            inherited;
        }
    }
    initNominalRoomPartLocation = defaultFloor
    isListedInRoomPart(part) { return isNominallyInRoomPart(part); }
;

++ pen: Hidden, Thing 'greenish green old-fashioned fountain nib/pen*pens' 
    'fountain pen'
    "It's an old-fashioned fountain pen, greenish in colour, <<broken ? 'with a
        broken nib' : 'but apparently in reasonable working order'>>. It's
    currently <<full ? 'full of ink' : 'empty, though'>>. "
    full = nil
    dobjFor(FillWith)
    {
        verify()
        {
            if(full)
                illogicalAlready('The pen is already full. ');
        }
    }
    iobjFor(PutIn) remapTo(FillWith, self, DirectObject)
    iobjFor(PourInto) remapTo(FillWith, self, DirectObject)
    cannotPushMsg = 'You\'ve never considered yourself a mere pen-pusher, and
        you don\'t intend to start now. '
    shouldNotBreakMsg = 'Violence towards innocent writing implements has never
        been one of your vices; besides, it\'s a fine old pen, and you\'re a
        respectable scholar. '
    cannotCleanMsg = 'You wipe a bit of dust off, but otherwise it doesn\'t
        really need cleaning. '
    cannotUnscrewMsg = 'There\'s no need to do that. '
    cannotScrewMsg = (cannotUnscrewMsg)
    cannotEatMsg = 'Chewing pens was a habit you left behind when you were a
        schoolboy. '
    isModern = (!full)
    broken = nil
    makeBroken()
    {
        broken = true;
        name = 'broken fountain pen';
        cmdDict.addWord(self, 'broken', &adjective);
    }
;

++ bronzeKey: Hidden, Key 'large old-fashioned bronze key*keys' 'large bronze key'
    "It looks a little old-fashioned in design, and of a sort to unlock
    something reasonably substantial. "
    isModern = true
;

++ chocolateBar: Hidden, Food, StopEventList
    'large sweet beelzebite devilishly delicious smooth brown chocolate
    bar/chocolate/wrapper' 
    'bar of chocolate'
    "The wrapper is marked <q>BEELZEBITE -- Devilishly Delicious Chocolate
    (made in Hell)</q>. The chocolate inside <<eaten ? 'is all gone' : 'looks
        as if it could well live up to the promise'>>. "
    eaten = nil
    isModern = true
    cannotEatMsg = 'You\'ve already eaten the chocolate; there\'s nothing left
        but the wrapper. '
    dobjFor(TakeFrom)
    {
        verify()
        {
            if(gIobj != self)
                inherited;
        }
    }
    iobjFor(TakeFrom)
    {
        verify()
        {
            if(gDobj == self 
               || (gDobj == nil && gTentativeDobj.indexWhich({x: x.obj_ == self })))
            {
                if(eaten)
                    illogicalNow('The wrapper is empty -- the chocolate has all
                        been eaten. ');                   
            }
            else 
                inherited;
        }
        check()
        {
            if(gDobj == self)
                failCheck('It\'ll be easier to carry the chocolate around if you 
                    leave it in its wrapper. ');
        }
    }
    dobjFor(Taste) asDobjFor(Eat)
    dobjFor(Open) asDobjFor(LookIn)
    dobjFor(Eat)
    {
        verify()
        {
            if(eaten)
                illogicalAlready(cannotEatMsg);
        }
        action()
        {
            eaten = true;
            initializeVocabWith('empty -');
            name = 'empty chocolate wrapper';
            "The first nibble proves so absolutely delicious that you find you
            have to take another and then another until the whole bar is
            rapidly consumed. But then it leaves a vile aftertaste in your
            mouth -- something resembling a mixture of pig's vomit, burnt
            charcoal and nitric acid -- and a sick feeling in your stomach. ";
            daemonID = new Daemon(self, &daemon, 1);
        }
    }
    daemonID = nil
    daemon { doScript; }
    endDaemon
    {
        if(daemonID != nil)
            daemonID.removeEvent();
        daemonID = nil;
    }
    eventList = [
        nil,
        '<.p>You feel a bit queasy -- perhaps it was something you ate. ',
        '<.p>It feels like someone is tying knots in your stomach. ',
        '<.p>You feel violently sick. ',
        '<.p>A feeling of intense nausea sweeps over you. ',
        '<.p>The nausea starts to pass. ',
        '<.p>Your stomach still feels a bit queasy and you vow never to touch
        another piece of demonic chocolate ever again. ',
        '<.p>The worst of the churning in your stomach seems to be over. ', 
        '<.p>Your stomach starts to settle, but it still feels like you ate
        something you shouldn\'t, and there\'s still a nasty taste in your
        mouth. ',
        {: endDaemon() }
    ]
    lookInDesc = "<<eaten ? 'There\'s nothing inside -- the chocolate\'s all
        gone' : 'Inside the wrapper is a large bar of smooth brown
            chocolate'>>. "
    smellDesc = "It smells as sweet as honey<< eaten ? ', even though the
        chocolate inside is all gone' : ''>>. "
    futileToKnockOnMsg = 'This has no discernible effect. '
    weight = (eaten ? 0 : 1)
;

++ Hidden 'broken bent battered silver candlestick/stick' 'broken candlestick'
    "The silver candlestick looks bent and battered from the rubble that's
    fallen on it. The top is broken off, and there seems to be no conceivable
    use for what remains -- unless you were to melt it down and sell the
    silver, but you suspect that there may not be that much of a market for
    silver in hell, and you doubt that money would be much use to you now
    anyway. "
    isModern = true
    iobjFor(AttackWith) { verify() { } }
    weight = 2
    bulk = 2
;

+ Decoration 'blue brown fragmentary pieces/mosaic/fragments/bits' 
    'fragments of mosaic'
    "It's impossible to make out from the fragments what the mosaic may once
    have depicted, but whatever it was seems to have contained a fair amount of
    blue and at least some brown, since these are the two most common colours
    among the bits of mosaic you can see. "
    isPlural = true
    notImportantMsg = 'The pieces of mosaic are too fragmentary to be worth fiddling with. '
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

+ Unthing 'roof/ceiling' 'roof'
    'There is no roof; the nave is open to the sky, so what was the roof must
    now be among the rubble lying on the floor. '  
;

//==============================================================================

examHall: Room 'Examination Hall' 'the examination hall'
    "This large examination hall has something of the air of a down-at-heel
    university about it. Although it is currently set out for an examination --
    albeit with a single desk for only one candidate -- it looks like it could
    equally well do duty as a lecture theatre, meeting room or gym. A polished
    oak door leads south, while on the opposite wall is a waist-high slot, next
    to which <<slotDoor.isOpen ? 'a large opening leads north' : 'is a
        door-sized panel in the wall'>>. Higher up on the same wall is a
    clock. "
    south = bunkerDoor
    north = slotDoor  
    out: ConditionalConnector { ->bunkerDoor
        ": north or south"
        @(slotDoor.isOpen)    
    }
    thinkHere = "This room depresses you; you've sat more than enough
        examinations in your time, and marked even more. But then, you reflect,
        Hell just wouldn't be Hell for you if it allowed you to escape exams
        for all eternity. "
    throwDests = [[southDirection, bunkerDoor]]
;

+ bunkerDoor : RoomPartItem, Door ->oakDoor 'smart polished oak door*doors' 
    'oak door'
    "In the bunker the oak door looked totally incongruous; here it fits the
    fusty setting perfectly. "
    initNominalRoomPartLocation = defaultSouthWall
    initDesc = "In the south wall is an oak door. "
;

+ Chair, CustomFixture 'single small circular 
    fixed seat/stool/chair*stools*seats*chairs' 
    'stool'
    "This small circular stool, clearly too small for the rear end of anyone
    over the age of seven, has evidently been designed to maximise the
    discomfort of anyone rash enough or unfortunate enough to sit on it. "
    feelDesc = "It feels about as rear-end-numblingly hard as it is possible for
        a seat to get. "
    cannotTakeMsg = 'It seems to be fixed in place behind the desk. '
    reachableObjects = [oldWoodenDesk] 
;

+ oldWoodenDesk: Platform, Heavy 'solitary plain old wooden sloping 
    desk/top*desks*tops' 'desk'
    "It's an old plain wooden desk with a sloping top. It has just about enough
    room to work on, and a single stool fixed in place behind it. "
    bulkCapacity = 5
    disambigName = 'old wooden desk'
    allowedPostures = [standing, sitting]
;

++ examPaper: Readable 'exam examination (question) paper*papers*exams' 'exam paper'
    "The paper is headed:\b
    <CENTER>UNIVERSITY OF GEHENNA\n
    QUALIFYING EXAM IN NEW TESTAMENT STUDIES\n</CENTER> Candidates should
    attempt THREE questions, except that candidates requiring eternal torment
    must answer either FOUR or FIVE questions depending on the intensity of
    torment desired. All candidates contemplating rising from the dead must
    answer question 2.\b
    There follows a list of six questions.<.p> "
    readDesc()
    {
        desc();
        described = true;
        local i = 0;
        foreach(local q in [q1, q2, q3, q4, q5, q6])
        {
            "<.p><b><<++i>>.</b> <<q.desc()>> ";
            q.described = true;
        }
    }
    dobjFor(Examine)
    {
        verify() { logicalRank(110, 'exam paper'); }
    }
    
    bulk = 2
    cannotWriteOnMsg = (cannotAnswerMsg)
    cannotAnswerMsg = '[Note: to attempt an answer to any of the questions on
        this paper, use the command ANSWER QUESTION <i>N</i>, where N is the
        number of the question. In this syntax QUESTION may be abbreviation to
        Q, e.g. ANSWER Q 1]'
    isModern = true
    futileToKnockOnMsg = 'Crack! Rustle!'
;

class ExamQuestion : Readable, Component
    dobjFor(Answer)
    {
        verify()
        {
            if(!gActor.canTouch(examBooklet))
                illogicalNow ('Your answers must be written in the exam
                    booklet, which you don\'t have to hand right now. ' );
            if(answered)
                illogicalAlready('{You/he} {has} already answered that
                    question. ');
            if(examBooklet.questionsAnswered.length > 2 )
                illogicalAlready('You\'ve already answered three questions; you
                    don\'t need to answer any more. ');
            
        }
        check() 
        {       
            if(answerCheck != nil)       
                failCheck(answerCheck);            
            
        }
        action { 
            answer(); 
            answered = true; 
            examBooklet.questionsAnswered += self;
            if(examBooklet.questionsAnswered.length > 2)
            {
                "<.p>You complete the final question with such a flourish that,
                with the final stroke of the pen, you snap the nib beyond
                repair. ";
                pen.makeBroken();
            }
        }
        preCond = [touchObj, penHeld, penFull]
    }
    answer() { }
    answerCheck = nil
    answered = nil
    vocabWords = '(exam) (examination) q/question*questions'
    isModern = true
    isQualifiedName = true
    dobjFor(KnockOn) 
    { 
        verify() 
            { illogical('You can\'t knock on an exam question. '); } 
    }
    nothingBehindMsg = 'It\'s probably sufficient to concentrate on its surface
        meaning. '
;

+++ q1: ExamQuestion 'first 1 -' 'Question 1'
    "Arrange the following scholars in alphabetical order of nationality:
    G&uuml;nther Bornkamm, Rudolph Bultmann, Martin Dibelius, Ernst
    K&auml;semann, Willi Marxsen, Karl Ludwig Schmidt, Albert Schweitzer,
    William Wrede. "
    answer()
    {
        "Since all the scholars named are German, you simply copy their names
        into the exam booklet. ";
    }
    pluralOrder = 1
;

+++ q2: ExamQuestion 'second 2 -' 'Question 2'
    "Translate and comment on the following: <<toGreek('ti estin eukopwteron, 
        egerqhnai ek twn nekrwn, h peripatein epi thj qalasshj')>>;"
    
    answer = "You start by writing your translation: <q>Which is easier, to be
        raised from the dead or to walk on the sea?</q>. Then, after a brief
        pause to think, you add, <q>This is loosely based on Mark 2.9, in which
        Jesus challenges his hostile onlookers to say which is easier, to tell
        the paralytic that his sins are forgiven, or to tell him to get up
        (note that the same Greek verb is used here as for <q>be raised</q> in
        the passage set), pick up his stretcher and walk. The Greek for <q>to
        walk on the sea</q> also closely resembles that used in the description
        of Jesus' aquatic perambulation at Mark 6.48. The passage set would
        therefore seem to be a pastiche based on various parts of Mark's
        gospel. On the face of it the question posed is between two
        impossibilities. But <q>to be raised from the dead</q> could be read as
        a <q>divine passive</q>, with God understood as the implied agent.
        Likewise, to walk on the sea is presumably a divine act, since both of
        Mark's sea miracles echo language used of Yahweh the God of Israel in
        the Old Testament. And according to Mark 10.27 <q>all things are
        possible for God</q>, so, in Markan terms, what we in fact have here is
        a choice between two things that are equally possible.</q>\b
        You feel quite pleased with yourself with being able to give such a
        sensible answer to such a peculiar question. "
    
    pluralOrder = 2
;

+++ q3: ExamQuestion 'third 3 - ' 'Question 3'
    "What contribution might EITHER Philonic exegesis OR rabbinic midrash make
    to unravelling the inner meaning of the works of EITHER (a) Karlheinz
    Stockhausen OR (b) Karlheinz Wortschlachter OR (c) Benedict Spinoza.
    (WARNING: Candidates who offer inappropriate answers to this question will
    be subject to cruel and unusual punishments -- even by hellish
    standards). "
    answerCheck = 'You decide you\'d probably do better to attempt a different
        question. '  
    pluralOrder = 3
;

+++ q4: ExamQuestion 'fourth 4 -' 'Question 4'
    "<q>Karlheinz Wortschlachter's contribution to the study of the New
    Testament already exceeds that of Rudolph Bultmann.</q> Discuss. "
    answer = "You write <q>No contest: Bultmann was one of the greatest scholars
        of the twentieth century. Wortschlachter is a buffoon.</q> "  
    pluralOrder = 4
;

+++ q5: ExamQuestion 'fifth 5 -' 'Question 5'                                                           
    "Reconstruct the Q parallel to Mark 16.1-8, noting the incidence of Mark-Q
    overlaps. Then write a commentary on the text in German using the methods
    of form criticism, source criticism, narrative criticism, reader-response
    criticism, socio-redaction criticism, ideological criticism, feminist
    criticism, Marxist-materialist criticism, and reception-history criticism,
    together with six further hitherto undiscovered critical methodologies you
    will need to invent for the purpose. Make absolutely sure (on pain of
    instant disqualification) that none of your German sentences has less than
    three subordinate clauses and that at least two-thirds of every page is
    taken up with footnotes. Ensure also that your commentary isolates at least
    sixteen redactional layers in Q, and gives a complete history of the Q
    community, including the precise number of left-handed Syrian feminist
    cynics it contained at each stage in its socio-historical development
    together with the precise percentage of them that could sensibly have been
    potential members of a first-century Jesus Seminar. "
    answer() { "In the exam booklet you write: <q>There is no Q parallel to
        Mark 16.1-8.</q> "; }
    pluralOrder = 5
;

+++ q6: ExamQuestion 'sixth 6 -' 'Question 6'
    "Name three Synoptic Gospels, listing them in strictly canonical order. "
    answer() { "You duly write: <q>Matthew, Mark, Luke</q> in the exam booklet. "; }
    pluralOrder = 6
;



++ examBooklet: Thing 'exam examination (answer) booklet/script/book' 'exam booklet'
    "The front of the booklet is marked UNIVERSITY OF GEHENNA -- EXAMINATION
    ANSWER BOOK. Inside are a number of pages for the writing of exam answers.
    <<answers()>>. <<annotation()>> "
    answers()
    {
        switch(questionsAnswered.length)
        {
        case 0:
            "Right now, all the pages are blank"; break;
        case 1:
            "In the book you have written the answer to 
            <<questionsAnswered[1].name>>"; break;
        case 2:
            "The book now contains your answers to
            <<questionsAnswered[1].name>> and <<questionsAnswered[2].name>>";
            break;
        case 3:
            "The first couple of pages of the book are inscribed with your
            answers to <<questionsAnswered[1].name>>,
            <<questionsAnswered[2].name>> and <<questionsAnswered[3].name>>";
            break;
        }
        
    }   
    annotated = nil
    annotation()
    {
        if(annotated)
        {
            "<.p>After your answers are some annotations in another hand:\n
            <q>This candidate has shown sufficient grasp of the basics to be
            allowed to continue down the Scholars' Path, but whether he has the
            ability to overcome the remaining challenges is doubftul.
            Nevertheless, he has managed to find a back way into hell, and the
            authorities have accordingly decreed that he must be fully put to
            the test.</q>\b";
            if(questionsAnswered.indexOf(q2))
                "<q>The candidate has answered question 2; if this means he is
                desirous of rising from the dead, it shows that he has not yet
                grasped his true situation.</q>\b";
            else
                "<q>The candidate did not attempt question 2. Perhaps he does not
                wish to rise from the dead -- or perhaps he has grasped the true nature
                of his situation.</q>\b";
            
            "<q>For this candidate to have any hope at all, he must not only
            persevere through the morass created by his profession in the
            twentieth century -- he must overcome it. Success is not
            prognosticated, but in the unlikely event that the candidate should
            survive the Scholars' path, we are confident that the mounting
            challenges that follow will soon make him abandon hope and give up
            on his heart's desire.</q>";
        }   
    }
    
    questionsAnswered = []
    bulk = 2
    dobjFor(WriteOn)
    {
        verify() { }
        action()
        {
            "If you want to answer one of the questions on the exam paper use
            ANSWER QUESTION <i>N</I> (or simply ANSWER Q <i>N</i>).\n
            Assuming that's what {you/he} want{s} to do, \v";
            askForDobj(Answer);
        }
    }
    dobjFor(WriteSomethingOn) remapTo(WriteOn, self)
    dobjFor(LookIn)
    {
        action() { answers(); annotation(); }
    }
    dobjFor(Open) asDobjFor(LookIn)
    isModern = (questionsAnswered.length == 0)  
;

+ slotDoor : SecretDoor ->brownDoor 'large flat door-sized panel' 'panel'
    desc()
    {
        if(isOpen)
            "The panel has slid aside revealing a large opening next to the
            slot. ";
        else
            "The flat panel is about the shape and size of a door, as well
            being in the right place for one, but you can't see any way of
            making it slide open. ";
    }
    makeOpen(stat)
    {
        if(stat)
        {
            name = 'opening';
            initializeVocabWith(name);
            brownDoor.makeLocked(nil);
        }
        inherited(stat);
    }
    
    
    //  destination = deconstruction
    initNominalRoomPartLocation = defaultNorthWall
    //  isListedInRoomPart(part) { return isNominallyInRoomPart(part); }
    cannotOpenMsg = 'You cannot see how to open it. '
    nothingBehindMsg = &cannotSeeBehindMsg
;

+ RestrictedContainer, Fixture 'waist-high waist high slot' 'slot'
    "The slot is set into the wall at about waist height, and is marked
    <q>Completed Exam Scripts</q>."
    validContents = [examBooklet]
    canPutIn(obj) { return inherited(obj) && obj.questionsAnswered.length > 2; }
    cannotPutInMsg(obj) {
        if(gIobj == self)
            return 'The slot seems intended only for completed exam scripts. ';
        else
            return nil;
    }  
    iobjFor(PutIn)
    {
        action()
        {
            if(examInserted)
            {
                gDobj.moveInto(nil);
                "{The dobj/he} disappears into the slot. ";
            }
            else
            {
                gDobj.moveInto(controlDesk.subSurface);
                "{The dobj/he} disappears into the slot. For a while, nothing
                seems to happen, then after several minutes there is a rumbling
                and a clicking from deep inside. Finally, after a further brief
                silence, there is a sudden creak in the wall next to the slot
                as a panel slides aside to reveal an opening. ";
                examBooklet.annotated = true;	     
                slotDoor.makeOpen(true);
                achievement.awardPointsOnce();
                examInserted = true;
            }
        }
    }
    achievement : Achievement { +2 "completing the exam and putting it in the
        slot" }
    nothingBehindMsg = &cannotSeeBehindMsg
    examInserted = nil
;

+ Distant 'old-fashioned round (wall) clock/dial' 'clock'
    desc()
    {
        local answers = examBooklet.questionsAnswered.length;
        "The clock is an old-fashioned round wall clock with Roman numerals
        round the dial. It indicates that ";
        if(answers < 3)
            "you have about <<timeLeft[answers + 1]>> minutes left to complete
            the exam. ";
        else
            "you should have completed the exam by now. ";       
    }
    timeLeft = ['forty', 'twenty', 'ten' ]
    tooDistantMsg = 'The clock is too high up for you to reach. '
    iobjFor(ThrowAt) { verify() { inherited Thing; } }
    initNominalRoomPartLocation = defaultNorthWall
    isListedInRoomPart(part) { return isNominallyInRoomPart(part); }  
    nothingBehindMsg = &cannotSeeBehindMsg
;

++ Component, Distant 'roman numerals' 'roman numerals'
    "They're distributed round the dial in just the order you'd expect: I II
    III IV V VI VII VIII IX X XI XII. "
    isPlural = true 
;

//=================================================================================

viaModernaCorridor : Room 'Short Corridor'
    "A mere fifteen feet separates the yellow door at the north end of this
    bare corridor from the brown door at its south end. In between the corridor
    nevertherless finds space for a blue door to the east. "
    south = brownDoor
    north = yellowDoor
    east = blueDoor  
    throwDests = [[eastDirection, blueDoor]]
;

+ brownDoor : RoomPartItem, IndirectLockable, Door 
    'sliding brown examination room (south) (s) door*doors' 'brown door'
    "\^<<theName>> (a sliding door) is <<marking>>. "
    initNominalRoomPartLocation = defaultSouthWall
    initSpecialDesc = "In the south wall is <<aName>> <<marking>>. "
    cannotMoveMsg = 'It\'s a sliding door. '
    marking = 'marked EXAMINATION ROOM'
;

+ blueDoor: RoomPartItem, Door 'blue control room (east) (e) door*doors' 
    'blue door'
    "\^<<theName>> is <<marking>>. "
    initNominalRoomPartLocation = defaultEastWall
    initSpecialDesc = "In the east wall is <<aName>> <<marking>>. " 
    marking = 'marked CONTROL ROOM'
    dobjFor(Push) asDobjFor(Open)
    dobjFor(Pull) asDobjFor(Close)
;

+ yellowDoor : RoomPartItem, IndirectLockable, Door 
    'yellow deconstruction danger dangerous chamber (north) (n) door*doors' 
    'yellow door'
    "\^<<theName>> is <<marking>>. "
    initNominalRoomPartLocation = defaultNorthWall
    initSpecialDesc = "In the north wall is <<aName>> <<marking>>. " 
    marking = 'marked DANGER -- NO EXIT -- DECONSTRUCTION CHAMBER'
    destination = deconstruction
    dobjFor(Push) asDobjFor(Open)
    dobjFor(Pull) asDobjFor(Close)
;


//==============================================================================

controlRoom: Room 'Control Room'
    "The main feature marking this out as a control room is the TV monitor
    fastened to the north wall, with a row of buttons directly beneath it. A
    solid steel desk juts up against the neighbouring wall, with a utilitarian
    padded steel chair fixed behind it. There's a blue door out to the west and
    a grey door to the north. "
    out asExit(west)
    west = controlDoor
    north = greyDoor
    throwDests = [[westDirection, controlDoor]]
;

+ controlDoor : RoomPartItem, Door -> blueDoor 
    'blue (west) (w) door*doors' 'blue door'
    dobjFor(Pull) asDobjFor(Open)
    dobjFor(Push) asDobjFor(Close)
    initNominalRoomPartLocation = defaultWestWall
    initSpecialDesc = "In the west wall is <<aName>>. "
;

+ greyDoor : RoomPartItem, LockableWithKey, Door 
    'grey gray staff only (north) (n) door*doors' 'grey door'
    "\^<<theName>> is <<marking>>. "
    marking = 'marked STAFF ONLY'
    initNominalRoomPartLocation = defaultNorthWall
    initSpecialDesc = "In the north wall is <<aName>> <<marking>>. "
    cannotOpenLockedMsg = 'The grey door seems to be locked. <.reveal
        greydoor-locked>'
    dobjFor(Push) asDobjFor(Open)
    dobjFor(Pull) asDobjFor(Close)
;

+ controlDesk: RoomPartItem, ComplexContainer, CustomFixture 
    'solid soviet fifties nineteen-fifties empire steel desk/table*desks*tables' 
    'steel desk'
    "It has something of a nineteen-fifties Soviet Empire look about it. "
    cannotTakeMsg = 'The desk is bolted firmly to the floor. '
    initNominalRoomPartLocation = defaultEastWall
    initSpecialDesc = "A solid steel desk nestles up against the east wall. "
    subSurface: ComplexComponent, Surface { bulkCapacity = 30   }
    subUnderside: ComplexComponent, Underside { bulkCapacity = 30   }
    cannotSitOnMsg = 'The padded steel chair would probably make a marginally
        better seat. '
    cannotLieOnMsg = 'It\'s not long enough. '
    cannotStandOnMsg = 'It\'s obviously strong enough, there\'s plenty of
        headroom, and the desk is easily large enough -- it\'s just patently
        obvious that there\'s no damned <i>point</i> in standing on the
        wretched thing. So you don\'t. '
    nothingBehindMsg = 'There\'s nothing behind the desk except the wall. '
;

++ Readable 'small note' 'small note'
    "The note reads <q>GONE TO LUNCH</q>. "
    dobjFor(WriteOn) { verify() { } }
    dobjFor(WriteSomethingOn) { verify() { } } 
    weight = 0
    subLocation = &subSurface   
;

+ Chair, CustomFixture 'padded faded utilitarian grey steel chair/padding' 
    'padded steel chair'
    "The chair has something of the same stark Soviet look as the desk, being
    strictly utilitarian in design and covered with faded grey padding. "
    cannotTakeMsg = 'The chair is bolted firmly to the floor. '
    reachableObjects = [controlDesk]
;

+ OnOffControl, CustomFixture 'tv screen (blank) 
    television monitor/screen/display/set' 'monitor'
    "The monitor comprises a large screen, together with a switch and a
    circular dial. <<isOn ? monitorPicture.desc() : 'The screen is blank. '>>"
    
    cannotTakeMsg = 'The TV monitor is fastened securely to the wall. '
    shouldNotBreak = 'It\'s built like a tank -- you can\'t even dent it. '
    uselessToAttackMsg = (shouldNotBreakMsg)
    initNominalRoomPartLocation = defaultSouthWall 
    nothingBehindMsg = 'You can\'t actually see behind the monitor, but it\'s 
        abundantly obvious that all that\'s there is the wall. '
    
    makeOn(val)
    {
        inherited(val);
        if(val)
        {
            "The monitor takes a moment or two to warm up, but a picture soon
            appears. ";
            PresentLater.makePresentByKeyIf('tv', {x: x.tvKey == tvDial.curSetting } );
        }
        else
        {
            "The monitor goes blank, and the picture vanishes. ";
            PresentLater.makePresentByKeyIf('tv', nil);
        }
        monitorPicture.makePresentIf(val);
    }
    soundDesc = "The monitor is silent, and you can't find a volume control.
        Either something is wrong with the set, or it was never designed for
        sound. " 
;

++ monitorPicture: PresentLater, Component 'picture/view' 'picture'
    "<<descPicture(tvDial.curSetting)>>"
    descPicture(val)
    {
        val = toInteger(val);
        switch(val)
        {
        case 1:
            return
                'The screen displays an old movie, which seems to have been
                shot in the old days of hell, when subjecting the inmates
                to eternal torture was all the rage.<.reveal torture-movie> ';          
        case 2:
            return 'The screen displays a view of a pipe sticking out of a
                steep rocky hillside. Your old f[r]iend the workdemon 
                is busily working on the pipe with a blow-torch and a grille. ';
            
        case 3:
            return 'The screen shows a view of Hell\'s Gate. Nothing is moving
                there, and the workdemon has gone, together with his ladder. ';
            
        case 4:
            return 'The screen shows a view of a woman with long blonde hair
                struggling vainly in a swamp that seems determined to suck her
                down. A closer look suggests that the woman could well be
                Felicity Hope. <.reveal felicity-picture> ';   
            
        case 5:
            return 'On the screen is what looks like some sort of chess
                championship in progress. The players seem to be in rather odd
                dress, and some of the chess pieces look a bit odd, but it\'s
                still recognizably a game of chess. <.reveal chess-screen>';
            
        case 6:
            return 'The screen displays a view of the examination hall, which
                looks much as you left it. ';
            
        case 7:
            return 'On the screen is a view looking across a garden at a stone
                blocking the entrance to a cave that could well be a tomb. You
                at once think of Mark 16.1-8 and Wortschlachter\'s lecture, but
                nothing seems to be happening on screen: no angels, no women
                coming to the tomb, and no one rising from the dead. ';
            
        case 9:
            return 'The screen shows a close-up view of Professor
                Wortschlachter still delivering his lecture. However long you
                watch, he continues to spout unabated; but at least there\'s no
                sound, which is one small mercy. ';
            
        case 10:
            return 'The screen shows a view across a wide chasm towards a
                distant golden glow. ';
            
        default:
            return 'It\'s just showing a commercial for ' + getAdvert() + '. ';
            
        }
    }
    advertList : ShuffledList {
        valueList = ['horn polish', 'Beelzebite
            Chocolate Bars', 'demythologization-surplus red-hot pokers',
            'a counselling service for demythologized demons', 
            'damned man\'s relish',
            'holidays in Diss', 'green slime' ]     
    }
    getAdvert()
    {
        return advertList.getNextValue();
    }
;

class PicturePart : PresentLater, Component
    plKey = 'tv'
    dobjFor(Examine) { verify() { logicalRank(120, 'current'); } }
;

+++ PicturePart 'old vile ghastly macabre eternal film/movie/torture' 'old movie'
    "Frankly, you think it's pretty vile; hell must be about the only place
    such stuff isn't banned. "
    tvKey = '1'
;

+++ PicturePart 'inmates' 'inmates'
    "You can't believe what's being done to them in the movie - at least, you
    don't want to believe it. The lack of sound somehow just makes it all the
    more macabre. "
    tvKey = '1' 
    isPlural = true
;

+++ PicturePart 'old friend/fiend/demon/workdemon/grille/pipe/blow-torch/torch' 
    'demon'
    "The workdemon seems to be using the blow-torch to weld the grille inside
    the pipe, presumably to stop anyone else getting into hell that way. "
    tvKey = '2'
    isHim = true
    isIt = true
;

+++ PicturePart 'hell\'s gate' 'Hell\'s Gate'
    "It doesn\'t look like much is happening there. "
    tvKey = '3'
    isProperName = true
;


+++ PicturePart 'struggling blonde felicity woman/felicity/hope/hair*women' 
    'blonde woman'
    "The more you look, the more certain you feel that she <i>is</i> Felicity
    Hope, and the more concerned you feel for her. You can't be sure whether
    you're watching a live scene, a recording, or simply a diabolical
    deception, but the longer you watch the more you instinctively feel that
    Felicity Hope is in terrible trouble and it's down to you to save her. "
    isHer = true
    tvKey = '4'
;

+++ PicturePart 'evil bubbling nasty morass/nastiness/marsh/swamp' 'swamp'
    "The swamp looks like an evil morass of bubbling nastiness. "
    tvKey = '4'
;


+++ PicturePart 'chess game/championship/chess/tournament' 'chess game'
    "The game may be near the beginning, still there are still a lot of pieces
    on the board. The game also seems to be going rather slowly. "
    tvKey = '5'
;

+++ PicturePart 'chess red white odd oddly-shaped pieces*pieces' 'chess pieces'
    "One set of pieces is red and the other white. Many of them look quite
    conventional, but there are also some that seem oddly-shaped, but the
    picture isn't clear enough to make out the details. "
    tvKey = '5'
    isPlural = true
;

+++ PicturePart 'chess ancient rather odd strange roman players/dress/toga' 
    'chess players'
    "They both seem to be in ancient dress: one in a Roman toga and the other
    in something rather more middle-eastern. "
    isPlural = true
    tvKey = '5'
;

+++ PicturePart 'examination exam hall' 'examination hall'
    "It's just as you left it; nothing has changed, and nothing is moving there. "
    tvKey = '6'
;

+++ PicturePart 'blocking stone' 'stone'
    "The stone just sits there resolutely not budging, as if it has comfortably
    settled down for the duration. Watching it is only marginally more exciting
    than a paint-drying exhibition. "
    tvKey = '7'
;

+++ PicturePart 'cave/tomb' 'cave'
    "The cave is probably a tomb of some sort. "
    tvKey = '7'
;


+++ PicturePart 'garden' 'garden'
    "The garden looks peaceful and pleasant enough, but you can't
    see much of it apart from the cave and the stone. "
    tvKey = '7'
;

+++ PicturePart 'dire commercials/adverts/advertisements' 'commercials'
    "You thought the adverts on TV back in the world of the living were
    bad enough, but these are positively dire. "
    isPlural = true   
    tvKey = '8'
;

+++ PicturePart 'prof prof. professor wortschlachter/man/lecturer' 
    'Professor Wortschlachter'
    "He just keeps talking, and talking, and talking, and talking and... "
    isProperName = true
    isHim = true
    tvKey = '9'
;


+++ PicturePart 'distant celestial glorious vague golden glow/city/blob' 
    'distant golden glow'
    "As you watch, the glow shifts in shape from a vague golden blob to a
    glorious golden city. This is obviously the golden glow you saw across the
    chasm, and seeing it again only renews your determination to find a way to
    get to it. "
    tvKey = '10'
;

+++ PicturePart 'wide uncrossable chasm' 'chasm'
    "The chasm looks as uncrossable as ever. "
    tvKey = '10'
;

++ tvDial: NumberedDial, Component 'circular round dial' 'dial'
    "It's a circular dial, numbered <<minSetting>> to <<maxSetting>>.
    It's currently set to <<curSetting>>. "
    curSetting = '6'
    actionDobjSetTo()
    {
        inherited;      
        if(location.isOn)
        {
            mainReport('{You/he} turn{s} the dial to ' + curSetting +
                       ', and the picture on the screen changes. '
                       + monitorPicture.descPicture(curSetting));
            PresentLater.makePresentByKeyIf('tv', {x: x.tvKey == curSetting } );
        }
    }
;

++ Switch, Component 'plain black on-off switch' 'black switch'
    "It's just a plain black on-off switch. "
    makeOn(val) { location.makeOn(val); }
    isOn = (location.isOn)
    dobjFor(Push) asDobjFor(Switch)
    dobjFor(Turn) asDobjFor(Switch)
    dobjFor(Pull) asDobjFor(Switch)
    
;

+ Collective, Fixture 'larged coloured colored row/buttons*buttons' 
    'row of buttons'
    "The row of buttons is just beneath the monitor; it comprises three
    buttons, one red, one green, one brown, running from left to right. "
    isCollectiveFor(obj) { return obj.ofKind(ControlButton); }
    dobjFor(Push)
    {
        verify() { }
        action()
        {
            "There are three buttons here: red, green and brown. ";
            askForDobj(Push);
        }
    }   
    cannotTakeMsg = 'The buttons are firmly fixed to the wall. '
    initNominalRoomPartLocation = defaultSouthWall
    nothingBehindMsg = &cannotLookBehindMsg
;


class ControlButton : Button, CustomFixture 'large *buttons' 'button'  
    dobjFor(Push)
    {
        verify() { logicalRank(140, 'button'); }
        action
        {
            buttonAction();
            "You hear a very distant click. ";
        }
    }    
    cannotTakeMsg = 'It\'s fastened firmly to the wall. '
    nothingBehindMsg = &cannotLookBehindMsg
    buttonAction() { }
;


+ ControlButton 'red button' 'red button'
    "The large red button is fixed to the wall under the monitor, to the left
    of the green button. "
    buttonAction {  yellowDoor.makeLocked(true); }  
    pluralOrder = 1
;

+ ControlButton 'green button' 'green button'
    "The large green button is fixed to the wall under the monitor, between the
    red button and the brown button. "
    buttonAction()  {  yellowDoor.makeLocked(nil); }    
    pluralOrder = 2
;

+ ControlButton 'brown button' 'brown button'
    "The large brown button is fixed to the wall under the monitor, to the
    right of the green button. "
    pluralOrder = 3
    buttonAction()
    {
        if(brownDoor.isLocked)
        {
            brownDoor.makeLocked(nil);
            brownDoor.makeOpen(true);
        }
        else
        {
            brownDoor.makeOpen(nil);
            brownDoor.makeLocked(true);
        }    
    }
;


//==============================================================================
deconstruction: Room 'Deconstruction Chamber' 'the deconstruction chamber'
    "This could almost be the inside of a rock-hewn tomb, except that where the
    exit should be is a huge screen on which is projected a constant procession
    of words, phrases, and fragments of text. Otherwise this space is curiously
    bare. "
    out: NoTravelMessage { "There's no obvious way out; perhaps you can think of
        an unobvious one. " }
    thinkHere()
    { 
        "Thinking indeed seems to be about the only thing you can do here. It
        occurs to you that you really need to think about what you need to
        think about. ";      
    }  
    achievement: Achievement { +1 "escaping the deconstruction chamber" }
    atmosphereList : ShuffledEventList {
    [
        'The words <q>midrashic intertexture of this text</q> flash up on the
        screen. ',
        
        {: "You catch sight of the words, <q><<nextFragment()>></q>" },
        
        'The phrase <q>any literalist reading of the empty tomb story can
        be instantly deconstructed</q> wanders across the screen. ',
        
        {: "A new verse fragment floats across the screen:
            <q><<nextFragment()>></q>. " },
        
        'You catch sight of the words, <q><i>Sitz im Leben</i> in the proper
        <i>religionsgeschichtlichen</i> context</q>. ',
        
        {: "On the screen appear the words: <q><<nextFragment()>></q>. "},
        
        'The words, <q>its thorough demythologization</q> twirl around on the
        screen. ',
        
        {: "<q><<nextFragment()>></q> slips slowly down the screen. " },
        
        'The phrase <q>the mythological transcending of this fundamental binary
        opposition</q> judders on the screen. ',
        
        {: "Across the screen wafts another fragment:
            <q><<nextFragment()>></q>" },
        
        'The words on the screen boldy proclaim, <q>all lesser purity
        boundaries between clean and unclean are henceforth abolished</q>.'   ,
        
        {: "Round and round and round rotate the words,
            <q><<nextFragment>></q>. " },
        
        'Something about <q>the allegorical techniques of Philonic exegesis</q>
        floats past on the screen. '   
        
    ]
        shuffleFirst = nil
        nextFragment() { return textFragment.getFragment(); }
    }
    
    escape()
    {     
        if(fuseID != nil)
        {
            fuseID.removeEvent;
            fuseID = nil;
        }
        if(daemonID != nil)
        {
            daemonID.removeEvent;  
            daemonID = nil;
        }
        nestedAction(TravelVia, tombGarden);
        achievement.awardPointsOnce();
    }
    cannotGoThatWay()
    {
        reportFailure('There\'s no obvious way out of here. It looks like
            you\'ll have to think of an unobvious one. '); 
    }
    enterRoom(traveler)
    {
        fuseID = new Fuse(self, &fireFuse, 10);
    }
    fuseID = nil
    daemonID = nil
    fireFuse()
    {
        fuseID = nil;
        daemonID = new Daemon(self, &execDaemon, 1);
    }
    execDaemon() { daemonList.doScript; }
    daemonList: StopEventList {
    [
        '<.p>It strikes you that although this chamber is all very interesting,
        it may be about time you found a way out of here. ',
        nil,
        '<.p>You start to wonder how much longer you can stay here without
        being deconstructed yourself. ',
        nil,
        '<.p>Observing the deconstruction of the Empty Tomb scene is all very
        well, but you reckon that it\'s time you started thinking about a way
        out of here. ',
        nil,
        lexicalParent.wayOutList
        
    ]
    }
    
    wayOutList : ShuffledEventList {
    [
        '<.p>You decide that it\'s definitely time to think about a way out. ',
        
        '<.p>You have no wish to remain here forever. ',
        
        '<.p>You really wish you could see a viable exit from here. ',
        
        '<.p>It must surely be time to think about an exit, you decide. '
        
    ]
        eventPerCent = 50
    }
    
    afterTravel(traveler, connector)
    {
        "<.p>As you walk through the yellow door, you catch one last sight of
        the words <q>NO EXIT</q> on it, before the door slams firmly shut,
        melding so perfectly with the wall that it leaves no sign that it was
        ever there. ";
    }
    
    suppressAtmosphere = nil
    roomDaemon
    {
        try
        {
            if(!suppressAtmosphere)
                inherited;
        }
        finally
        {
            suppressAtmosphere = nil;
        }
    }
;

+ Noise
    isAmbient = nil
    displaySchedule = [2, 4, 4, 8]
    hereWithSource = "A disembodied voice proclaims, <q>There is nothing
        outside the text.</q> "
;

+ dScreen: RoomPartItem, CustomFixture 'large huge screen' 'huge screen'
    "On the screen you see the words: <q><<textFragment.getFragment()>></q>"
    
    initNominalRoomPartLocation = defaultNorthWall
    isListedInRoomPart(part) { return isNominallyInRoomPart(part); }  
    futileToKnockOnMsg = 'Your knock is instantly deconstructed. '
    cannotTakeMsg = 'The screen is fixed firmly to the wall. '
    nothingBehindMsg = &cannotSeeBehindMsg
    nothingThroughMsg = 'You can see what\'s on the screen, but you can\'t
        see through it. '
    uselessToAttackMsg = 'You don\'t even dent it. '
    shouldNotBreakMsg = 'They sure know how to make tough screens in hell --
        this one appears to be unbreakable. '
    cannotCleanMsg = 'It looks clean enough already. '
    cannotTurnOnMsg = 'From the way it\'s behaving, it would be a reasonable
        assumption that the screen is already turned on. '
    cannotTurnOffMsg = 'It must be controlled from outside the chamber; at
        least, you can see no way of turning it off from here. '
    cannotEatMsg = 'No: the screen just deconstructed your appetite. '
    notFollowableMsg = 'You might find it a hard act to follow, assuming you
        can follow what\'s going on on the screen, which is in any case more
        than capable of deconstructing your notion of following, since it
        isn\'t in motion, but you\'re not really sure whether it\'s going
        somewhere or not -- if you can follow all that. '
    
#ifdef __DEBUG
    verifyDobjPow { logicalRank(150, 'locator'); }
#endif
;

+ textFragment: Vaporous 'swirling words/phrases/fragments/text' 'text'
    "One fragment of text reads <q><<getFragment()>></q>. "
    getFragment() { return fragmentList.getNextValue();  }
    fragmentList : ShuffledList {
        valueList = [
            'they said nothing to anyone, for they were afraid -- <i>so how
            does anyone know what they saw?</i>',
            
            'and when they entered the tomb they saw a young man sitting on the
            right dressed in a white robe, and they were astounded -- <i>why
            the surprise? The youth of the man? The colour of the robe?</i>',
            
            'And he said to them, <q>Don\'t be so surprised...</q> -- <i>of
            course not, coming to a tomb where you\'ve just seen a loved one
            buried and then finding it empty shouldn\'t surprise anyone.</i>',
            
            '<q>... You\'re looking for Jesus the crucified Narazene...</q> --
            <i>smart deduction; this is where the guy was buried just a couple
            of days ago, where else would you expect to find him?</i>',
            
            '<q>... he is risen, he is not here; see the place where they put
            him,</q> -- <i>Hm. Nasty cold slab of stone, not comfy to lie on at
            all, even if you\'re dead; could that be why...</i>', 
            
            '<q>But go and tell his disciples and Peter...</q> -- <i>Hang on!
            Wasn\'t Peter a disciple too? Or did he stop being one when he
            denied Jesus?</i>',
            
            '<q>He is going before you to Galilee, and there you will see him,
            just as he told you.</q> -- <i>But Mark never describes this
            reunion inside his text, so within the text this is never
            fulfilled.</i>',
            
            'And when the Sabbath came, Mary Magdalene and James\'s Mary and
            Salome -- <i>Who are these women? Why don\'t they appear in Mark\'s
            narrative at all until Jesus\' death?</i> bought spices so that
            might come and anoint him -- <i>but we all know they\'re too late
            for that, don\'t we; some nameless chick got there first a couple
            of chapters back.</i>', 
            
            'Very early, on the first day of the week, they came to the tomb
            just as the sun was rising.' ,
            'And they said to one another, <q>Who will roll the stone away for us?</q>
            -- <i>too bad they didn\'t think about that before they set out!</i>',
            
            'And when they looked up they saw that the stone had been rolled
            away -- <i>how convenient!</i> -- for it was very large -- <i>come
            again, it was rolled away because it was large?</i>',
            
            'And they came out and fled in fear and terror -- <i>Who
            wouldn\'t?</i>'
            
        ]
    }
    isPlural = true  
    dobjFor(Read) asDobjFor(Examine)
    dobjFor(Examine)
    {
        action()
        {
            inherited;
            deconstruction.suppressAtmosphere = true;
        }
    }
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

+ Unthing 'projector' 'projector'
    '{You/he} see{s} no sign of one here; the images must be projected onto the
    screen from the other side. '
;

//==============================================================================
tombGardenSouthWall : RoomPart 's south rocky slope/wall*walls' 'south wall'
    "The south wall of the garden is formed by a rocky slope in which is set
    the mouth of a cave. "
;

tombGarden: OutdoorRoom 'Walled Garden' 'the walled garden'
    "The main feature of this small walled garden is the tomb formed by a cave
    just to the south, from the mouth of which the stone has just been rolled
    away. <<westLook>>"
    //  west = viaModerna
    south = tomb
    west = tombGardenPath
    in asExit(south)
    roomParts = [defaultGround, defaultSky, defaultEastWall, defaultNorthWall,
        tombGardenSouthWall]
    vocabWords = 'walled garden'
    thinkHere = "This garden seems strangely tranquil -- you certainly can't
        detect anything hellish about it -- or anything particularly twentieth
        century for that matter. "
    southLook = "To the south lies the mouth of a tomb. "
    westLook = "A path leads off through the bushes towards a tall tree at the
        west end of the garden. "
;

//+ tombGardenGate : AutoClosingDoor 'gate' 'gate'
//;

+ Enterable ->tomb 'tomb/cave/mouth*tombs*caves*mouths' 'tomb'
    "The tomb is a cave set into the south wall of the garden. The stone
    designed to cover the mouth of the tomb has been rolled aside, allowing
    access to the cave. "
    disambigName = 'mouth of tomb'
    nothingBehindMsg = &cannotSeeBehindMsg
;

+ Heavy 'large round stone*stones' 'stone'
    "The large round stone is about six feet in diameter. It appears to have
    some sort of marking on it. "
    nothingBehindMsg = &cannotSeeBehindMsg
;

++ Component 'marking*markings' 'marking'
    "The stone is marked: Mark 16.1-8"
;

+ Distant 'tall tree/top*trees*tops' 'tall tree'
    "The top of the tall tree can be seen poking above the bushes to the west. "
;

+ tombGardenPath: PathPassage 'path' 'path'
    "The path leads off through the bushes to the west. "
    nothingBehindMsg = &unclearWhatBehindMsg
;

MultiLoc, Fixture 'low green centre/clumps/bush/(garden)*bushes' 'bushes'
    "Clumps of low green bushes grow in the centre of the garden. "
    locationList = [tombGarden, tombGardenWest]
;

tgwGround : defaultGround 'soft thick green carpet/grass' 'ground'
    "A thick carpet of grass covers the ground at this end of the garden. "
;

//==============================================================================
tombGardenWest : OutdoorRoom 'West End of Garden' 'the west end of the garden'
    "<<travelOrigin == tombGarden ? 'The path from the east comes to the end at
        the foot a tall tree that towers majestically over you, casting its
        shade over the soft green grass' : 'The light and tranquility of this
            garden stand in stark contrast to the gloom of the path to Hinnom
            Valley outside, just as the tall tree that dominates this end of
            the garden stands as a living contrast to the dead crosses on the
            distant hill across the valley. From the tree, a path leads
            eastwards through the bushes in the centre of the garden'>>. The
    garden is walled in to north, west and south, but a small black door is set
    into the north wall. "
    east = tombGardenWestPath
    north = smallBlackDoor
    roomParts = [tgwGround, defaultSky, defaultNorthWall, defaultSouthWall,
        defaultWestWall]
    vocabWords = 'west end/garden'
    thinkHere = "The west end of the garden strikes you as being just as innocuous
        as the east end. "
    eastLook = "A path leads through the bushes to the east. "
;

+ tombGardenWestPath: PathPassage ->tombGardenPath 'path' 'path'
    "The path leads off through the bushes to the east. "
    nothingBehindMsg = &unclearWhatBehindMsg
;

+ smallBlackDoor: RoomPartItem, LockableWithKey, Door 'small black door*doors' 
    'small black door'
    "The small black door is set into the north wall of the garden. "
    keyList = [bronzeKey]
    cannotOpenUnlockedMsg = 'It would appear to be locked. <.reveal
        black-door-locked>'
    lockStatusObvious = (gRevealed('black-door-locked'))
    initNominalRoomPartLocation = defaultNorthWall
    initSpecialDesc = "In the north wall is a small black door. "
;

+ Fixture 'tall majestic tree*trees' 'tall tree'
    "The tall tree is laden with strange fruit of a kind you have never seen
    before and cannot identify. "
    cannotClimbMsg = 'The lowest branch is far too high up for you to reach. '
;

++ Distant 'lowest stout and long branch*branches' 'lowest branch'
    "The lowest branch is stout and long, so long that it reaches over the west
    wall of the garden. It is also far too high up for you to reach. "
;

++ Distant 'strange round spherical red pink grey gray black fruit' 'fruit'
    "It's unlike any other fruit you have ever seen. Each fruit looks perfectly
    spherical, but, strangely, the fruit seems to come in four different
    colours: red, pink, grey and black. "
    disambigName = 'strange round fruit'
    dobjFor(Smell) { verify() { } }
;

+++ SimpleOdor 'sweet scent' 'sweet scent'
    "A sweet scent wafts from the fruit. "
;
//==============================================================================
hinnomPath : OutdoorRoom 'Path to Hinnom Valley' 'the path to the valley'
    "<<northLook>>In contrast to the garden, this is a bleak, barren place,
    with dark black rocks strewn all around. Immediately to the south the path
    comes to an end at a wall, through which is a small black door. "
    roomParts = [defaultGround, defaultSky, defaultSouthWall]
    north : FakeConnector { "You set off down the path, but the further you go,
        the hotter it becomes; the Valley of Hinnom smoulders with eternal
        flames, and it soon becomes apparent that there's no way through
        without getting roasted, so you turn round and climb back up the path." }
    south = hinnomPathDoor
    thinkHere = "Thinking about this place gives you the shivers. The valley
        below is the very prototype of hell, and your barren surroundings seem
        to epitomise the emptiness of eternal death. "
    northLook = "From here a rugged path leads down into Hinnom Valley below,
        and then up a steep hillside on the other side of the valley. "
    defaultLook(dir)
    {
        "Dark black rocks lie strewn all around the bleak hillside. ";
    }
;

+ PathPassage 'narrow rugged path' 'rugged path'
    "It runs down into the valley below. "
    dobjFor(TravelVia) remapTo(North)
    nothingBehindMsg = &unclearWhatBehindMsg
;

+ hinnomPathDoor: RoomPartItem, LockableWithKey, Door 
    ->smallBlackDoor 'small black door' 'small black door'
    "It's set into the wall. "
    keyList = [bronzeKey]
    lockStatusObvious = true
    initNominalRoomPartLocation = defaultSouthWall
    initSpecialDesc = "A small black door leads through the wall to the south. "
;

+ Decoration 'dark black rocks' 'dark black rocks'
    "The dark black rocks do not so much strew the hillside as constitute it,
    stretching as far as you can see to east and west, with the narrow path to
    the north constituting the only viable way across them. "
    isPlural = true
;

+ hinnomValley: Distant 'hinnom valley/hinnom/gehenna' 'Valley of Hinnom'
    "The Valley of Hinnom (aka Gehenna) stretches out below you, smouldering
    with eternal flames burning up all the refuse and rubbish of time. "
;

+ Distant 'eternal smouldering flames' 'eternal flames'
    "The flames down in the valley do not make a particularly impressive sight;
    they simply carry on smouldering. "
    isPlural = true
    dobjFor(Smell) remapTo(SmellImplicit)
;

+ Distant 'burning refuse/rubbish/trash' 'rubbish'
    "Precisely what rubbish is being burned down in the valley can hardly be
    discerned from here. "
;

+ Distant 'steep distant far hill/hillside' 'steep hill'
    "The far side of the valley seems higher than this one, so that the steep
    hillside in the distance rises to an impressive height above you. In the
    far distance atop the hill you can just make out three crosses silhouetted
    against the grey skey. "
;

+ Distant 'three distant t-shaped bare crosses/cross' 'crosses'
    "The three crosses on top of the distant hill are all bare; if anyone was
    crucified there they have evidently since been taken down. Unlike the
    &dagger;-shaped crosses typically pictured in Christian art and Church
    iconograpghy, these are T-shaped, probably more like the ones the Romans
    actually used for executions. "
;

+ SimpleOdor 'stench/burning' 'stench of burning'
    "A stench of burning rises from the valley. "
    isAmbient = nil
    displaySchedule = [2, 4, 8]
;

+ SimpleNoise 'profound utter sound/silence' 'silence'
    "The silence here is the silence of the grave -- profound and utter. "
;

//==============================================================================
beadGroup : ListGroupSorted
    compareGroupItems(a, b) 
    { return a.pluralOrder > b.pluralOrder ? 
            1 : a.pluralOrder == b.pluralOrder ? 0 : -1; }
    
;

beadCollective : CollectiveGroup '(two) (three) (four) colored coloured *beads' 
    'coloured beads'
    
    isCollectiveAction(action, whichObj)
    {
        /* we handle 'Examine' */
        if (action.ofKind(GiveToAction) || action.ofKind(ShowToAction))
            return true;
        
        /* it's not one of ours */
        return nil;
    }
    
    /*
     *   Check to see if we're a collective for the given quantity.  By 
     *   default, we return true only when no quantity is specified.  
     */
    isCollectiveQuant(np, requiredNum)
    {
        /* if no quantity was specified, use the collective */
        return true;
    }
    dobjFor(ShowTo) asDobjFor(GiveTo)
    dobjFor(GiveTo)
    {
        verify()
        {
            if((gIobj && gIobj == whiteYouth)
               || (gIobj == nil 
                   && gTentativeIobj.indexWhich({x:x.obj_ == whiteYouth})))
                
                illogical('The young man asked you to give him only one bead.' );
        }
    }
;

tomb: Room 'Inside the tomb'
    "The interior of the tomb is about ten feet square, with rough-hewn
    limestone walls and curved corners. There is just about enough headroom to
    stand in the depression at the centre, but most of the space is taken up
    with a stone shelf running round three of the walls at waist height. In the
    wall above the shelf to the west is a niche or <i>loculus</i>. To the north
    <<tombStone.isOpen ? 'light streams in through the mouth of the cave, which
        provides the only exit' : 'a stone has been rolled across the mouth of
            the cave, leaving you no way out'>>. "
    enterRoom(traveler)
    {
        if(!gRevealed('criteria'))
        {
            "As you step into the tomb, the stone rolls itself against the
            mouth of the cave, blocking your exit. Fortunately, the shining
            white robes of the young man continue to provide ample light. ";
            tombStone.makeOpen(nil);
        }
    }
    north = tombStone
    out asExit(north)
    vocabWords = 'tomb/cave'
    thinkHere = "Well, you can hardly doubt what this place is meant to be:
        a representation of the tomb of Jesus. "  
    roomBeforeAction()
    {
        if(gActionIs(Jump))
        {
            "You calculate that the probability of a jump bringing your head
            into painful contact with the ceiling is roughly one hundred
            percent, so you wisely decide against it. ";
            exit;
        }
    }
;

+ Decoration 'rough-hewn rough limestone walls' 'walls'
    "<<tombStone.initSpecialDesc>> <<niche.initSpecialDesc>> "
    dobjFor(Examine)
    {
        verify() { logicalRank(50, 'stand-in'); }    
    }
    isPlural = true
    notImportantMsg = 'They\'re best left alone. '
;


+ tombStone : RoomPartItem, SecretDoor 
    'large opening/stone/boulder*stones*boulders' 'stone'
    "The stone is rolled <<isOpen ? 'aside' : 'over the mouth of the cave'>>. "
    destination = lakeSide
    initiallyOpen = true
    initNominalRoomPartLocation = defaultNorthWall
    initSpecialDesc()
    {
        if(isOpen)
            "In the north wall is an opening large enough to go through. ";
        else
            "A large stone has been rolled against the opening in the north
            wall, blocking the exit. ";
    }
    cannotMoveMsg = 'It\'s too heavy; you can\'t budge it. '
    acceptCommand(issuingActor)
    {
        "The stone proves deaf to your entreaties. ";         
        return nil;
    }
    notAddressableMsg = 'The stone ignores {it actor/him}. '
    dobjFor(KnockOn) 
    { 
        verify() 
        { 
            if(isOpen)
                illogicalNow(&cannotKnockOnHoleMsg); 
        } 
    }
    nothingBehindMsg = &cannotSeeMsg
;

+ Bed, Fixture 'flat stone shelf/rock' 'shelf of rock'
    "The shelf runs round three sides of the tomb. It's about three feet high
    and two wide. "
    cannotStandOnMsg = 'There\'s insufficient headroom. '
    nothingBehindMsg = 'Behind the shelf is the wall. '
;

class Bead : Thing
    colour = ''
    vocabWords = (colour + ' small round smooth bead*beads')
    name = (colour + ' bead')
    desc = "It's a smooth, round, <<colour>> bead. "
    listWith = [beadGroup]
    collectiveGroup = beadCollective
;

++ redBead : Bead
    colour = 'red'
    pluralOrder = 10
;

++ pinkBead : Bead
    colour = 'pink'
    pluralOrder = 20
;

++ greyBead: Bead
    colour = 'grey'
    vocabWords = static ('gray ' + inherited)
    pluralOrder = 30
;

++ blackBead: Bead
    colour = 'black'
    pluralOrder = 40
;

+ niche : RoomPartItem, Container, Fixture 'niche/loculus' 'niche'
    "The niche (or <i>loculus</i>, to use the technical term) is basically
    a hole cut into the western limestone wall. "
    initNominalRoomPartLocation = defaultWestWall
    initSpecialDesc = "A loculus, i.e. a niche, has been cut into the west wall. "
    cannotEnterMsg = 'It\'s far too small for a living person to enter; the
        only way anyone can get in there is as a pile of disconnected bones. '
    nothingBehindMsg = &cannotLookBehindMsg
;

++ ossuary : ComplexContainer 'limestone (stone) (bone) 
    box/ossuary*boxes*ossuaries' 'ossuary'
    "The ossuary is a rectangular box made of limestone, about fifteen inches
    long by ten wide. On its front are inscribed a rosette, a menorah and the
    word <q>SCHOLARSHIP</q>. "
    initDesc = "It's a rectangular stone box; while it remains in its niche
        that's about all that can be seen of it. "
    subContainer: ComplexComponent, IndirectLockable, OpenableContainer {
        dobjFor(Open)
        {
            verify()
            {
                if(isIn(niche))
                    illogicalNow('The ossuary can\'t be opened while it\'s in
                        the niche. ');
                inherited;
            }
        }
        bulkCapacity = 4
        cannotUnlockMsg = 'There must be some special knack to it. <.reveal
            ossuary-lock>'
        dobjFor(Examine) remapTo(Examine, ossuary)     
    }
    
    dobjFor(PutIn)  {  preCond = static inherited + objClosed  }
    
    dobjFor(Pull) maybeRemapTo(isIn(niche), Take, self)
    dobjFor(Break)
    {
        preCond = [objHeld]
        verify() { }
        action()
        {
            reportFailure('{You/he} {is} about to dash {the dobj/him} on the
                floor, when it occurs to {it actor/him} that this would be an
                act of totally pointless vandalism. ');
        }
    }
    weight = 8
;

+++ criteria: Readable 'criteria scroll/paragraphs*scrolls' 'scroll'
    "The scroll contains three paragraphs explaining the criteria for
    authenticity. "
    readDesc = "CRITERIA FOR AUTHENTICITY:\b
        (1) <i>Double Dissimilarity:</i> to meet this criterion a bead must be
        unlike at least two other beads.\b
        (2) <i>Coherence:</i> any bead which is found to resemble beads
        identified as authentic by criterion 1 is itself more likely to be
        authentic.\b
        (3) <i>Multiple Attestation</i>: a bead is more likely to be authentic
        if it is similar to as many other beads as possible.<.reveal criteria>"
    bulk = 2 
    subLocation = &subContainer
    dobjFor(WriteSomethingOn) asDobjFor(WriteOn)
    dobjFor(WriteOn)
    {
        preCond = [penHeld, penFull]
        verify() { }
        action() {
            "{You/he} decide{s} that defacing it might be a bad idea, since
            {its actor/her} scribbles would probably make it harder to read. ";
        }
    }
    disambigName = 'criteria scroll'
    weight = 2
;

+++ menorah: Hidden, Button, Component 
    'neatly rendered seven-branched menorah/candlestick' 'menorah'
    "It's a seven-branched candlestick, neatly rendered on the front of
    ossuary. This is no crude carving -- or even well-executed carving -- as
    you might have expected; instead it stands out in relief from the surface
    of the ossuary. "
    discovered = (!ossuary.isIn(niche))
    pushed = nil
    dobjFor(Push)
    {
        action() {
            "There's a distinct click. ";
            pushed = true;
        }
    }  
    cannotMoveMsg = &cannotMoveThatWayMsg
    weight = 0
;

+++ rosette : Hidden, Component 'carved round rosette/flower' 'rosette'
    "It's a representation of a round flower. Like the menorah, it stands out
    in relief instead of being carved into the limestone of the ossuary, as you
    would have expected. "
    discovered = (!ossuary.isIn(niche))
    dobjFor(Turn)
    {
        verify() { }
        check() {
            if(!menorah.pushed)
            {
                reportFailure('It looks like it might turn but something seems to
                    be preventing it. ');
                exit;
            }      
        }
        action()
        {
            "{You/he} turn{s} the rosette through a quarter-circle";
            if(ossuary.subContainer.isLocked)
                " and hear another satisfying click; at the same time you detect
                a slight movement in the lid";
            ".";
            ossuary.subContainer.makeLocked(nil);
        }
    }
    cannotMoveMsg = &cannotMoveThatWayMsg
    verifyDobjMove { illogical('You\'ll have to be more specific. '); }
    weight = 0
;

+++ Hidden, Component 'neatly incised word scholarship*words' 
    'word <q>scholarship</q>'
    "The word <q>SCHOLARSHIP</q> is neatly incised on the side of the
    ossuary. "
    discovered = (!ossuary.isIn(niche))
    weight = 0
;

+++ Hidden, ContainerDoor '(ossuary) lid*lids' 'ossuary lid'
    "<<ossuary.desc>> Its lid is plain. "
    discovered = (!ossuary.isIn(niche))
    weight = 1
;
//==============================================================================

class Lake: Fixture
    dobjFor(Swim)
    {
        verify() { }
        check()
        {
            reportFailure('The lake looks far too wide to swim. ');
        }
    }
    dobjFor(Drink)
    {
        verify() {}
        preCond = [touchObj]
        action() {
            "You cup your hands and draw up some water from the lake, sipping
            it messily. It tastes reasonably fresh, but a little bitter. ";
        }
    }
    dobjFor(Taste) asDobjFor(Drink)
    dobjFor(Cross) asDobjFor(WalkOn)
    dobjFor(WalkOn) { verify() { nonObvious; } }
    dobjFor(Enter) asDobjFor(Swim)
    lakeCross(yes)
    {
        if(yes)
            replaceAction(WalkOn, self);
        else
            "No, I thought not. ";
    }
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
    iobjFor(ThrowAt) asIobjFor(ThrowInto)
    iobjFor(ThrowInto)
    {
        verify() {}    
        action()
        {
            if(gDobj.weight > 4)
            {
                "{The dobj/he} {is} too heavy to throw very far, and fall{s}
                short of the lake. ";
                nestedAction(Drop, gDobj);
            }
            else if(gDobj.weight < 1)
            {
                "{The dobj/he} {is} too light to travel any distance, since the
                ratio of mass to wind resistance is insufficient to carry it
                through the air. So {the dobj/he} simply flutter{s} to the
                ground. ";
                nestedAction(Drop, gDobj);
            }
            else
            {
                "{The dobj/he} splash{es} into the water a good distance from
                the shore, and sink{s} out of sight. ";
                gDobj.moveInto(nil);
            }
        }
    }
    lookInDesc = "The lake is full of water. "
    nothingThroughMsg = 'You can\'t see much through the water. '
    nothingUnderMsg = 'You can\'t make out what lies under the water. '
    nothingBehindMsg = 'Presumably the far shore lies behind the lake, though
        it\'s too far away to see much of it from here. '
;


lakeSide : OutdoorRoom 'Lakeside' 'the lakeside'
    "All around is a desolate wilderness, with sparse grass growing in thin,
    sandy soil<<walkCount == 0 ?', otherwise featureless apart from a small
        cave nearby, which you could go back in' : ''>>. The wilderness
    stretches over an empty plain towards the southern horizon, while directly
    to the north is a large lake, the shore of which stretches east and west as
    far as the eye can see. "
    east : TravelMessage { ->lakeSide
        "You <<lexicalParent.walkCount == 0 ? 'head off' :
          (lexicalParent.walkCount < 0 ? 'return' : 'continue')>> eastwards
        along the shore of the lake, hoping that you might find some way to
        skirt it, but the shore seem to have no end, stretching as far to the
        east as you can see however far east you walk.
        <<lexicalParent.walk(1)>>"
        isCircularPassage = true     
    }
    myCave = tomb
    in = (walkCount==0 ? myCave : nil)
    north : NoTravelMessage 
    { 
        "Do you really think you can walk on water? <<yesNo()>>" 
        yesNo() {  yesNoResponse.set(lake, &lakeCross); }
    }
    west: TravelMessage { ->lakeSide
        "You wander along the shore of the lake for what seems like miles and
        miles and miles, but there seems to be no end either to the lake or to
        the wilderness.<<lexicalParent.walk(-1)>>"
        isCircularPassage = true     
    }
    south : TravelMessage { ->wilderness
        "You stroll away from the lake across what seems to be a never-ending
        wilderness. "
    } 
    southeast : TravelMessage { ->wilderness
        "You strike off to the southeast across the vast empty plain.
        <<lexicalParent.walk(1)>>"
    }
    southwest: TravelMessage { ->wilderness   
        "You march boldly off to the southwest across the great barren
        wilderness. <<lexicalParent.walk(-1)>>"  
    }
    walk(easting)
    {
        walkCount += easting;
        wilderness.walkEast += easting;
    }
    northeast asExit(north)
    northwest asExit(north)
    vocabWords = 'lakeside'
    thinkHere = "You feel that this place has a different quality to it, as if
        by reaching it you have almost found freedom -- but perhaps it's just
        the wide open space. The trouble is, there's so little of interest
        here, apart from the lake. "
    
    walkCount = 0
    
    /* 
     *   We're using the lakeSide room to represent a large number of rooms, 
     *   so we have to handle the possibility that the PC may drop things 
     *   here, which should only be found again here if he comes back to 
     *   what is notionally the same location.
     */
    
    enteringRoom(traveler)
    {
        foreach(local cur in contents)
            if(isPortable(cur) && cur.lakeDrop != walkCount)
            cur.moveInto(lakesideDepository);
        
        foreach(local cur in lakesideDepository.contents)
            if(isPortable(cur) && cur.lakeDrop == walkCount)
            cur.moveInto(self);  
    }
    roomAfterAction()
    {
        if(gDobj!= nil && gDobj.isDirectlyIn(self))
            gDobj.lakeDrop = walkCount;
    }
    northLook = "To the north the lake stretches to a distant shore. "
    eastLook = "The lake shore stretches as far as you can see to the east. "
    westLook = "The shore of the lake runs all the way to the western horizon. "
    northeastLook = "You look out over the water. "
    northwestLook = (northeastLook)
    defaultLook(dir)
    {
        "To the <<dir.name>> stretches a desolate wilderness as far as you can
        see. ";
    }
    throwDests = [[northDirection, lake], [northeastDirection, lake],
        [northwestDirection, lake] ]
;

+ Hidden, Enterable -> (location.in) 
    'small lakeside open cave/hump/mouth*caves*mouths*humps' 
    'small cave'
    "The small cave is little more than a hump in the ground with an open mouth
    just large enough to squeeze into. "
    discovered = (location.walkCount == 0)
    lookInDesc = "Peering into cave you see that it all looks much as
        you left it. "   
;

+ Fixture 'shore/shoreline*shores' 'shore'
    "The shoreline runs east and west as far as the eye can see, forming a
    sharp boundary marking out the binary opposition of land and sea. "
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
    nothingBehindMsg = 'Behind the shore is the sea -- or the wilderness inland -- 
        depending which way you\'re looking and which direction you count as
        <q>behind</q>. '
;

+ Distant 'southern distant horizon*horizons' 'southern horizon'
    "The southern horizon is a distant line where the empty plain meets the
    sky. "
;

+ Distant 'empty level deserted flat plain/wilderness*plains' 'empty plain'
    "The plain to the south is almost completely flat, and seems to be utterly
    deserted all the way to the distant horizon. "
    dobjFor(Enter) remapTo(South)
;

+ Decoration 'sparse green grass' 'sparse grass'
    "It's sparse, grass and just about green; enough to cover the ground but
    probably not enough to sustain a herd of cattle for any great length of
    time. Since, however, there's nothing to suggest the proximity of so much
    as a single cow, this does not strike you as something to worry about all
    that much. "
    notImportantMsg = '{The dobj/he} is so unbelievably unimportant that it\'s a
        wonder it ever occurred to {you/him} to attempt that. '
    isMassNoun = true
;

+ Decoration 'thin sandy soil' 'thin sandy soil'
    "It's on the ground and the sparse grass struggles to grow in it -- or at
    least, to survive in it. "
    isMassNoun = true
;

+ lake : Lake 'large huge calm blue lake/sea/water*lakes*seas' 'lake'
    "The large blue lake stretches so far to the north you can hardly see the
    other side. It also stretches as far as you can see to east and west. But
    if it looks huge, the lake also looks pretty calm. "  
    dobjFor(WalkOn)
    {    
        action()
        {
            if(gActor.contents.length == 0)
            {  
                "With great aplomb, you triumphantly stride over the waves,
                trampling them underfoot as if in a dream. You continue
                strolling confidently across the lake until you near the other
                side. Then the sheer absurdity of what you're doing strikes
                you; after all, walking on water is <i>impossible</i>. This
                realization jerks you into full consciousness, whereupon you
                start to panic and flounder. Fortunately, you are now close
                enough to the far shore that you sink only up to your waist,
                and manage to wade the last few dozen yards or so to the
                shore. ";
                achievement.awardPointsOnce();
                replaceAction(TravelVia, valley);
            }
            else
                "You take a few cautious steps, and for the first foot or so it
                seems to you that you could indeed be walking on the water, but
                then worldly thoughts start to weigh you down, and your feet
                sink to the bottom. You wade a few steps further into the
                water; when the water reaches your knees, you turn round and
                head back for the shore, wetter but possibly wiser. ";
        }
    }
    achievement : Achievement { +1 "walking across the lake" }
    
;

+ Distant 'green far other side/hint/green' 'other side'
    "You can just about make out a hint of green on the far side of the lake,
    but it's so far away you can't quite be sure what's there. "
;

+ SimpleNoise 'gentle sound/lapping*waves' 'gentle lapping'
    "Nothing but the gentle lapping of waves on the shore. "  
;

+ SimpleOdor 'fresh bracing air' 'fresh air'
    "The air here smells fresh and bracing. "
;

/* 
 *   No actor ever enters this room; it's purely for objects dropped by the 
 *   lakeside.
 */

lakesideDepository: Room
    
;
//==============================================================================

wilderness: OutdoorRoom 'Wilderness'
    "This <<epithet>> wilderness stretches as far as you can see to north,
    south, east and west. There's nothing here but a vast flat, empty plain,
    with not so much as a <<absent>> in sight. Although you're aware that
    there's a lake <<walkSouth < 3 ? 'somewhere' : 'a long way'>> off to the
    north, the view looks pretty much the same whichever way you face.  "
    north: TravelMessage { -> (lexicalParent.walkSouth == 0 ?lakeSide : wilderness)
        "You trudge northwards back across the featureless plain <<
          lexicalParent.walkSouth==0 ? 'and eventually arrive back at the shore
              of the lake' : 'but it still looks much the same'>>.
        <<lexicalParent.walk(-1,0)>>"      
        isCircularPassage = (lexicalParent.walkSouth != 0)
    }
    east : TravelMessage { -> wilderness
        "You stroll eastwards across the wilderness, but it still all looks
        much the same. <<lexicalParent.walk(0,1)>>"
        isCircularPassage = true      
    }
    west : TravelMessage { -> wilderness
        "You walk westwards across the plain for what seems miles and miles and
        miles, but there's no change in the scenery.
        <<lexicalParent.walk(0,-1)>>"
        isCircularPassage = true      
    }
    south : TravelMessage { -> wilderness
        "You walk southwards for miles across the unchanging
        plain.<<lexicalParent.walk(1,0)>>"
        isCircularPassage = true     
    }
    southeast : TravelMessage { ->wilderness
        "You amble some way to the southeast, but the plain remains unchanging.
        <<lexicalParent.walk(1,1)>> "
        isCircularPassage = true     
    }
    southwest : TravelMessage { ->wilderness   
        "You head off to the southwest across the barren plain, but find that
        nothing much changes. <<lexicalParent.walk(1,-1)>> "
        isCircularPassage = true     
    }
    northeast: TravelMessage { -> (lexicalParent.walkSouth == 0 ?lakeSide : wilderness)
        "You stroll northeast across the featureless plain <<
          lexicalParent.walkSouth==0 ? 'and eventually arrive back at the shore
              of the lake' : 'but it still looks much the same'>>.
        <<lexicalParent.walk(-1,1)>>"      
        isCircularPassage = (lexicalParent.walkSouth != 0)
    }
    northwest: TravelMessage { -> (lexicalParent.walkSouth == 0 ?lakeSide : wilderness)
        "You walk northwest across the featureless plain <<
          lexicalParent.walkSouth==0 ? 'and eventually arrive back at the shore
              of the lake' : 'but it still looks much the same'>>.
        <<lexicalParent.walk(-1,-1)>>"      
        isCircularPassage = (lexicalParent.walkSouth != 0)
    }
    walk(southing, easting)
    {
        walkEast += easting;
        lakeSide.walkCount += easting;
        walkSouth += southing;
        if(walkSouth < 0) walkSouth = 0;
    }
    
    walkEast = 0
    walkSouth = 0
    
    enteringRoom(traveler)
    {
        foreach(local cur in contents)
            if(isPortable(cur) 
               && !(cur.wildernessEast == walkEast && cur.wildernessSouth == walkSouth))
            cur.moveInto(wildernessDepository);
        
        if(!wildernessNote.moved && walkEast * walkEast + walkSouth * walkSouth > 16 )
        {
            wildernessNote.wildernessEast = walkEast;
            wildernessNote.wildernessSouth = walkSouth;
        }
        
        foreach(local cur in wildernessDepository.contents)
            if(isPortable(cur) 
               && cur.wildernessEast == walkEast && cur.wildernessSouth == walkSouth)
            cur.moveInto(self);  
    }
    roomAfterAction()
    {
        if(gDobj!= nil && gDobj.isDirectlyIn(self))
        {
            gDobj.wildernessEast = walkEast;
            gDobj.wildernessSouth = walkSouth;
        }
    }
    defaultLook(dir)
    {
        "This featureless plain continues much the same as far as you can see
        to the <<dir.name>>. ";
    }
    epithets = [ 'featureless', 'barren', 'deserted', 'trackless',
        'seemingly endless', 'interminable', 'great open', 'boundless', 'huge' ]
    
    epithet = (epithets [idx(walkEast + walkSouth, epilength)])
    epilength = static epithets.length()
    
    absentList = ['hillock', 'rabbit', 'grasshopper', 'dandelion', 'squirrel',
        'molehill', 'small bush' ]
    absentLength = static absentList.length
    absent = (absentList [idx(walkEast - 2 * walkSouth,  absentLength)]) 
    
    idx(x, len)
    {
        local val = (x % len) + 1;
        if(val < 1)
            val = len + val;
        return val;
    }
;

wildernessDepository : Room
;

/*
 *   If the player wanders too far into the wilderness he'll come across 
 *   this note, as a hint that there's not too much point in exploring it 
 *   any further.
 */
+ wildernessNote: Readable 'small pink writing/scrap/paper*scraps*papers' 
    'scrap of pink paper'
    "It's a small scrap of paper with some writing on it. "
    dobjFor(Read) { preCond = [objVisible, objHeld] }
    readDesc = "It reads: <q>There is no end to this empty wilderness.</q> "
    wildernessEast = 100000
    wildernessSouth = 100000
    initSpecialDesc = "A scrap of pink paper lies on the ground. "
    isInInitState = (!taken)
    taken = nil
    dobjFor(Take) { action() { inherited; taken = true; } } 
    dobjFor(WriteOn) { verify() { } }
    dobjFor(WriteSomethingOn) { verify() { } } 
    weight = 0  
;

//==============================================================================
blueSky : defaultSky 'brilliant blue sky/blue' 'blue sky'
    "The sky above is a brilliant blue: perhaps a bit too brilliant to be
    natural. "
;

grassGround : Floor 'grassy thick green ground/floor/grass/turf' 'ground'
    "The ground is covered with thick green grass that is surprisingly even. "
;

valley: OutdoorRoom 'Verdant Valley' 'the valley'
    "After rising lazily from the lake shore to the south this long valley
    continues north between steep slopes that bar progress to east and west.
    Underfoot and all around the grass lies green and thick, while high above
    the sky is a brilliant blue. "
    north = fork
    south : NoTravelMessage 
    { 
        "You don't seriously think you can walk on water <i>again</i> do you?
        <<yesNo>> ";
        yesNo() {  yesNoResponse.set(valleyLake, &lakeCross); }
    }
    roomParts = [grassGround, blueSky]
    thinkHere()
    {
        "At first sight, this lush green valley looks like the epitome of peace
        and tranquility, yet something seems not quite right. It seems to you
        that this valley only gives the <i>appearance</i> of life. It's too
        still, too quiet, to be natural. It looks an idyllic spot, but you
        sense that the idyll is somehow illusory, and an involuntary shudder
        electrifies your spine. ";
    }
    vocabWords = 'long verdant valley'
    southLook = "To the south lies the shore of the great lake. "
    northLook = "The long valley continues some way to the north. "
    defaultLook(dir)
    {
        "To the <<dir.name>> you see part of the steep slopes that enclose the
        valley. ";
    }
    throwDests = [[southDirection, valleyLake], [southeastDirection, valleyLake],
        [southwestDirection, valleyLake] ]
;

+ Fixture 'steep (east) (e) (west) (w) green slopes/slope' 'steep green slopes'
    "The steep green slopes rise up behind the trees on either side of you. "
    cannotClimbMsg = 'The slopes are to steep to climb. '
    isPlural = true
    nothingBehindMsg = &cannotLookBehindMsg
;

+ Decoration 'beech trees' 'trees'
    "They might be beech trees, but you're not quite sure. Whatever they are,
    they line either side of the valley. "
    isPlural = true  
;


+ valleyLake : Lake 'large blue lake/sea/water' 'lake'
    "The large blue lake stretches out of sight to the south, and as far as you
    can see to east and west. "  
    dobjFor(WalkOn) 
    {    
        action()
        {
            "It must have been a one-way trick, for you now find that you have
            absolutely no idea how to walk on the lake. Your first, few
            faltering steps simply take you wading into it; now that you
            reflect consciously on the possibility of walking on water, you
            know it to be impossible and find you cannot do it. ";       
        }
    }    
;

+ SimpleNoise 'sound/silence' 'sound of silence'
    "It is strangely quiet here; the only sound is the sound of silence. "
;

+ SimpleOdor 'odourless odorless fresh air' 'air'
    "Here the air smells odourless and fresh. "
;

//==============================================================================

fork : OutdoorRoom 'Parting of the Ways' 'the parting of the ways'
    "The main part of the valley lies to the south. From here a broad,
    pleasant, easy slope leads down to the northeast, while a narrow steep and
    rugged pathway leads up the craggy hillside to the northwest. "
    south = valley
    northeast = forkSlope
    down asExit(northeast)
    northwest = forkPath
    up asExit(northwest)
    thinkHere()
    {
        "A couple of verses of scripture spring to mind, Matthew 7.13-14:
        <q>Enter by the narrow gate; for the gate is wide and the way is easy
        that leads to destruction, and those who enter by it are many. For the
        gate is narrow and the way is hard, that leads to life, and those who
        find it are few.</q>";
    }
    northwestLook = "To the northwest a steep and rugged pathway leads up the
        craggy hillside, looking arduous and thoroughly uninviting. "
    northeastLook = "A broad, grassy slope falls gently away to the northeast. " 
    westLook = "To the west towers a steep, rugged hillside. "
    southwestLook = "To the southwest the hillside tapers down to the steep
        slope that lines the west side of the valley. "
    southLook = "To the south lies the main part of the valley. "
    northLook = "To the north lie the foot of the hills that tower up to the
        northwest. "
;

+ forkSlope : StairwayDown ->gladeSlope 'broad pleasant easy grassy slope*slopes' 
    'broad slope'
    "The grassy slope leads gently down towards the northeast. "
    noteTraversal(traveler)
    {
        "You set off down the gentle slope, finding it an easy and inviting
        stroll that becomes easier and more inviting with every step, still
        after a while you eventually arrive at its foot. ";
    }
;

+ forkPath : TravelMessage, StairwayUp, StopEventList 
    'narrow steep bleak rugged uninviting craggy
    pathway/hillside/hill/mountain/path*paths*hills*pathways*hillsides*mountains' 
    'steep pathway'
    "Running sharply up the bleak, rugged hillside, the narrow path looks
    thoroughly uninviting, though more arduous than perilous. The hill itself
    could well turn out to be a mountain if you climbed all the way up; you
    can't see the top from here so it's hard to tell. "
    destination = ruggedPathway
    eventList = [
        
        'Even at the start, the path is steep, and it seems to get steeper and
        harder the higher you climb. At first, the mountainside seems quite
        fertile, but with every step you take it grows more barren, while the
        light grows dimmer. With each step up the mountainside the path feels
        more bleak, and more hostile, until by the time you\'re halfway up the
        path you feel forced to take a rest, unsure whether you can summon the
        strength to continue. ',
        
        'Once again, you find the path becomes steeper, darker and bleaker the
        higher you climb. ' 
    ]
    dobjFor(Follow) asDobjFor(TravelVia)
    dobjFor(Take) asDobjFor(TravelVia)
    dobjFor(Enter) asDobjFor(TravelVia)
;

+ Enterable, Distant ->valley 'main part/valley' 'main part of the valley'
    "The <<name>> stretches south from here, running between gentle slopes
    towards the shore of the lake. "
;

+ Distant 'blue blue/glint/shore/lake*lakes*shores*glints' 'lake'
    "From here the lake is little more than a glint of blue at the far end of
    the valley to the south. "
;

//==============================================================================

footOfSlope: OutdoorRoom 'Sleepy Hollow' 'the sleepy hollow'
    "The gentle slope down from the west comes to an end in this sleepy little
    hollow next to a sheer wall of rock to the east. A wide green gate leads
    into the rockface. "
    west = gladeSlope
    up asExit(west)
    east = greenGate
    in asExit(east)
    thinkHere = "You reflect on how drowsy you feel, and how pleasant it would
        be to lie down and sleep for a very, very long time. "
    eastLook = "To the east is a sheer wall of rock, in which is set a broad
        green gate. "
    westLook = "A gentle slope leads up to the west. "
    defaultLook(dir) { "Everywhere you look you seem to be surrounded by wild
        flowers. "; }
;

+ gladeSlope : TravelMessage, StairwayUp 'gentle grassy slope*slopes' 'gentle slope'
    "The grassy slope rises up to the west in a gentle slope. "
    travelDesc = "You climb back up the gentle slope. "
    destination = fork
    
;

+ greenGate : Door 'wide broad wooden green gate/door*doors*gates' 
    'wide green gate'
    "It's about four feet wide and made of wood. Attached firmly to the gate
    is a sign that reads <<greenDoorSign.text>>. "
;

++ greenDoorSign : Component 'sign*signs' 'sign'
    "The sign reads <<text>>. "
    text = "<q>Herein will you find your rest</q>"
;

+ Fixture 'rough sheer rock wall/rock/face/rockface*walls*faces' 'rock wall'
    "Too steep to climb, the rough rock wall rises sheer for fifty or sixty
    feet, blocking all progress east except through the conveniently-placed
    green gate. "
    nothingBehindMsg = &cannotLookBehindMsg
    cannotClimbMsg = 'The sheer rockface is too steep to climb. '
;

+ SimpleOdor 'gentle sweet soporofic smell' 'smell'
    "A gentle, sweet soporofic smell pervades the air here. "
;

+ SimpleNoise 'like quiet drowsy chimes/sound' 'chimes'
    "A sound like quiet chimes fills the glade, almost silent, emanating from
    nowhere in particular, but inducing drowsiness everywhere. "
    isPlural = true
;

+ Decoration 'wild yellow flowers/daffodils' 'wild flowers'
    "There seems to be quite a mixture, and you can't identify many of them
    with confidence, but you recognize quite a few daffodils among them. "
    isPlural = true
;

//==============================================================================
oblivionChamber : Room 'Restful Lobby'
    "This pleasant, softly lit chamber has a green door leading out to the
    west, and a second, plush red door, leading east. An even plusher red sofa
    lines the wall facing it, with a small table set to one side. "  
    west = greenDoorOut
    out asExit(west)
    east = redDoor
    thinkHere = "You think of rest, of sleep, of the end of all striving. You
        think the comfortable red sofa looks very tempting, and the plush red
        door even more so. "
    vocabWords = 'pleasant restful softly-lit lobyy/chamber'
    eastLook()
    {
        inherited;
        if(redDoor.isOpen)
            "<<redDoor.nothingThroughPassageMsg>>";
    }
    throwDests = [[westDirection, greenDoorOut]]
;

+ SimpleNoise 'continual quiet slow restful music' 'music'
    "Continual music -- quiet, slow, restful music -- wafts lazily round the
    chamber, oozing from some concealed speaker to weave its sleepy spell on
    your ears and brain. "
;

+ SimpleOdor 'scented subtly soporific perfume/air/scent' 'scent'
    "The air is distinctly scented here, with some subtly soporific perfume. "
;

+ greenDoorOut: RoomPartItem, Door -> greenGate 'green door*doors' 'green door'
    
    initNominalRoomPartLocation = defaultWestWall
    initSpecialDesc = "In the west wall is a green door. "
;

+ redDoor : RoomPartItem, TravelWithMessage, Door 'plush red door*doors' 
    'red door'
    "The red door is clearly marked REST ROOM, and has a large sign attached to
    it. "
    dobjFor(Open)
    {
        verify()
        {
            if(isOpen)
                inherited;
            else
                dangerous;        
        }
    }
    destination = oblivion  
    travelDesc = "You walk through the door and step into ... "
    nothingThroughPassageMsg = '{You/he} {can} see absolutely nothing through
        the red door. '
    initNominalRoomPartLocation = defaultEastWall
    initSpecialDesc = "In the east wall is a plush red door, which is <<redDoor.isOpen
          ? 'open' : 'closed'>>. "
;

++ Component 'large sign*signs' 'large sign'
    "The large sign reads:\b  
    <q>Beyond this door is no more striving:\n from beyond this door shall none
    return</q>. "
;

+ Bed, Heavy 'plush large plusher red comfortable sofa/couch/settee/lounger
    *sofas*couches*settees*loungers*furniture' 'plush red sofa'
    "The sofa is large and thickly cushioned. "
    reachableObjects = [smallTable]
;


+ smallTable: Surface, CustomImmovable 'small wooden plain flat 
    table/top*tables*tops*furniture' 
    'small wooden table'
    "It's just a small wooden table with a plain flat top. "
    cannotTakeMsg = 'It seems to be fixed in place somehow; at least, you\'re
        unable to budge it. '
    cannotStandOnMsg = 'It may not take your weight. '
    cannotSitOnMsg = 'Why sit on an uncomfortable little wooden table, when the
        plush red sofa just nearby looks so much more tempting? '
    cannotLieOnMsg = 'It\'s too small to lie on. '
    shouldNotBreakMsg = 'You\'ve no reason to do that. '
;

++ Openable, RestrictedContainer 'black slim leather document folder*folders' 
    'black folder'
    "It's a slim, black leather document folder. "
    validContents = [brochure]
;

+++ brochure: Readable 'colourful brochure/text*brochures' 'colourful brochure'
    "It\'s titled ETERNAL REST  -- THE PROS AND CONS. "
    readDesc = "Inside are several glossy illustrated pages setting out the
        pros and cons of total oblivion. Although the diagrams are many, the
        pithy quotations frequent, and there is lavish use of side-bars,
        multi-coloured type, and apparently earnest discussion, even the most
        casual read-through reveals that the actual content boils down simply
        to this:\b    
        <i>Pros of oblivion:</i> No more pain, no more toil, no more sorrow, no
        more fear.\n
        <i>Cons of oblivion:</i> No more life, no more love, no more joy, no
        more HOPE\b
        The last of these points is illustrated by a particularly striking
        picture at the end. "
    dobjFor(Read)
    {
        preCond = static inherited + objHeld
    }
;

++++ Component 'elaborate diagrams' 'diagrams'
    "Although fairly elaborate, the diagrams and pictures that adorn the
    brochure add nothing of substance to its basic message. The message they
    convey is essentially identical to that in the text: oblivion gives one
    eternal escape from pain, toil, sorrow and fear but eternal forfeiture of
    life, love, joy and hope. "
    isPlural = true
    dobjFor(KnockOn) { verify() { illogical(&cannotKnockOnHoleMsg); } }
;

++++ Component 'particularly striking blonde felicity end evil-looking evil looking
    picture/woman/hope/felicity/swamp' 
    'particularly striking picture'
    "The picture shows Felicity Hope drowning in an evil-looking swamp, with an
    expression of heart-rending mute appeal on her face. <<noHope>>"
    initDesc = "<<noHope>><<rename()>> <.reveal felicity-picture>"  
    isInInitState = (!described)
    noHope = 'Across the picture is written NO MORE HOPE in large letters. '  
    rename()
    {
        name = 'picture of Felicity';
        "You glance at the picture and see a blonde woman struggling -- or
        drowning -- in an evil-looking swamp. With a start you realize that the
        woman is Felicity Hope, and then it strikes you that the expression on
        her face is one of mute appeal -- to you. ";
    }
;     

+++++ Component 'large letters' 'large letters'
    "<<location.noHope()>>"
    isPlural = true
;

//==============================================================================
oblivion: Room 'Oblivion'
    "You have entered oblivion: no more pain, no more striving, no more
    disappointment, no more fear; no more joy, no more love, no more life, no
    more hope. There is absolutely nothing here, since absolutely nothing can
    exist here, including you. "
    enterRoom(traveler)
    {
        finishGameMsg('YOU HAVE CEASED TO EXIST', 
                      [finishOptionUndo, finishOptionFullScore]);
    }
    vocabWords = 'oblivion'
;

grammar adjWord(sAndR) :
    'steep' 'and' 'rugged'
    : SpecialNounPhraseProd
    getMatchList = [forkPath]    
    getAdjustedTokens =
    ['steep', &adjective, 'and', &adjective, 'rugged', &adjective]
;

