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

/* 
 *   The main purpose of this file is to contain the code that's not 
 *   specific to any actor or region in the game; it contains new action 
 *   definitions, modified VerbRules, and other modifications to the 
 *   standard library behaviour, as well as the instructions menu.
 */



/*
 *   #define __TESTING
 */

/* First some new VerbRules and Actions */


VerbRule(Think)
    'think' | 'cogitate' | 'ponder' | 'th'
    :ThinkAction
    verbPhrase = 'think/thinking'
;

DefineIAction(Think)
    execAction()
    {
        gActor.getOutermostRoom.thinkHere();
    }
;

VerbRule(PrayImplicit)
    'pray'
    : PrayImplicitAction
    verbPhrase = 'pray/praying'
;

DefineIAction(PrayImplicit)
    execAction()
    {
        replaceAction(PrayTo, god);
    }
;


VerbRule(PrayTo)
    'pray' 'to' singleDobj
    : PrayToAction
    verbPhrase = 'pray/praying (to whom)'
;

DefineTAction(PrayTo)  
    objInScope (obj) { return obj.ofKind(Thing) && gPlayerChar.knowsAbout(obj); }
    isGlobalScope = true
    filterAmbiguousNounPhrase (lst, requiredNum, np) 
    {    
        //    lst = lst.subset({x: gActor.knowsAbout(x.obj_) });
        
        if(lst.length == 1)
            return lst;
        
        lst = filterAmbiguousDobj (lst, requiredNum, np);
        if(lst.length == 1)
            return lst;
        
        local shortlist = lst.subset({x: gActor.canSee(x.obj_) });
        if(shortlist.length > 0)
        {
            lst = shortlist;
            if(lst.length == 1)
                return lst;
        }   
        
        shortlist = lst.subset({x: !x.obj_.ofKind(Decoration)});
        if(shortlist.length > 0)
        {
            lst = shortlist;     
        }       
        
        return lst;
    }
    
;

VerbRule(PrayFor)
    'pray' ('for' -> prep_| 'about'-> prep_) singleTopic
    :PrayForAction
    verbPhrase = 'pray/praying (for what)' 
;

DefineTopicAction(PrayFor)
    execAction()
    {
        "{You/he} pray{s/ed} <<prep_>> <<subject()>>; whether it {will do|did}
        the remotest good remains to be seen. ";
    }
    subject()
    {
        local obj = gTopic.getBestMatch();
        if(obj && obj.ofKind(Thing) && gPlayerChar.knowsAbout(obj))
            return obj.theName;
        else
            return gTopicText;
    }
    prep_ = 'for'
;


VerbRule(WakeUp)
    'wake' ('up' |) | 'awake'
    : WakeUpAction
;

DefineIAction(WakeUp)
    execAction()  {
        
        switch(gActor.getOutermostRoom)
        {
        case lectureRoom:
            "Despite Wortschlachter's best efforts, you haven't actually
            fallen asleep. "; break;
        case hospital:
            "That precisely what you've just done. "; break;
        case breakfastQueue:
            "It may have been quite early, but you did manage to wake up
            <i>before</i> coming down to breakfast this morning! "; break;
        default:
            "{You/he} may be dead, but {it actor/he} do{es}n't appear to be
            asleep. ";
        }
    }
;


VerbRule(Concentrate)
    'concentrate' | 'focus' | 'pay attention'
    : ConcentrateAction
    verbPhrase = 'concentrate/concentrating'
;

DefineIAction(Concentrate)
    execAction()
    {
        "{You/he} {does} {its actor/her} best to concentrate on the matter in
        hand. ";
    }
;



VerbRule(Xyzzy)
    ('say' | ) ( 'xyzzy')
    :XyzzyAction
    verbPhrase = 'say/saying XYZZY'
;

DefineIAction(Xyzzy)
    execAction()
    {
        switch(gActor.getOutermostRoom)
        {
        case lectureRoom:
            "As much as you wish you did have a magic word to transport you out
            of here, this one doesn't seem to work. "; break;
        case breakfastQueue:
            "You muttered <q>Xyzzy, xyzzy</q> under your breath, which earned
            you a slightly odd look from the blonde woman, but otherwise
            achieved nothing. Perhaps it was the way you said it -- though
            strangely enough you had yet to find that it actually made a
            breakfast queue move any faster. "; break;
        case hospital:
            "Finding yourself alive seems quite magical enough. ";   
            break;
        default:
            "Your futile utterance scarcely disturbs the air; such a magic word
            would not work in the real world, and it seems it doesn't work here
            either. ";
        }
    }
;

DefineTIAction(TakeWith)
;

VerbRule(TakeWith)
    'take' singleDobj 'with' singleIobj
    : TakeWithAction
    verbPhrase = 'take/taking (what) (with what)'
;

DefineTAction(Cross)
;

VerbRule(Cross)
    (('go' | 'walk') ('across' | 'over' )| 'cross') singleDobj
    : CrossAction
    verbPhrase = 'cross/crossing (what)'
;

DefineTAction(Hold)
;

VerbRule(Hold)
    'hold' singleDobj
    : HoldAction
    verbPhrase = 'hold/holding (what)'
;



DefineTAction(WalkOn)
;

VerbRule(WalkOn)
    'walk' ('on' | 'onto') singleDobj
    :WalkOnAction
    verbPhrase = 'walk/walking (on what)'
;

DefineTAction(Swim)  
;

VerbRule(Swim)
    'swim' ('in' | ) singleDobj
    : SwimAction
    verbPhrase = 'swim/swimming (what)'
;

DefineTAction(Answer)
;

VerbRule(Answer)
    'answer' singleDobj
    : AnswerAction
    verbPhrase = 'answer/answering (what)'
;

DefineIAction(Breathe)
    execAction
{
    gActor.breathe();
}
;

VerbRule(Breathe)
    'breathe'
    : BreatheAction
    verbPhrase = 'breathe/breathing'
;

DefineTIAction(FillWith)
;

VerbRule(FillWith)
    'fill' singleDobj 'with' singleIobj
    : FillWithAction
    verbPhrase = 'fill/filling (what) (with what)'
;

VerbRule(FillWithWhat)
    [badness 500] 'fill' singleDobj
    :FillWithAction
    verbPhrase = 'fill/filling (what) (with what)' 
    construct()
    {
        /* set up the empty indirect object phrase */
        iobjMatch = new EmptyNounPhraseProd();
        iobjMatch.responseProd = withSingleNoun;
    }
;


DefineTAction(WriteOn)
;

VerbRule(WriteOn)
    'write' ('in' | 'on') singleDobj
    : WriteOnAction
    verbPhrase = 'write/writing (on what)'
;

DefineLiteralTAction(WriteSomethingOn, DirectObject) 
;

VerbRule(WriteSomethingOn)
    'write' singleLiteral ('in' | 'on') singleDobj
    : WriteSomethingOnAction
    verbPhrase = 'write/writing (what) (on what)'
;

VerbRule(TalkAbout)
    'talk' ('to' | ) singleDobj 'about' singleTopic
    : TellAboutAction
    verbPhrase = 'talk/talking (to whom) (about what)'
    askDobjResponseProd = singleNoun
    omitIobjInDobjQuery = true
;

DefineIAction(LieDown)
    execAction()  
    {
        if(gActor.posture == lying)
        {
            reportFailure('{You/he} {are} already lying down. ');
            exit;
        }
        askForDobj(LieOn);
    }
    
;

VerbRule(LieDown)
    'lie' (| 'down')
    :LieDownAction
    verbPhrase = 'lie/lying down'
;

/* Add some synonyms to existing actions */

modify VerbRule(AttachTo)
    ('attach' | 'connect' | 'fix' | 'affix' | 'tie' ) dobjList 'to' singleIobj
    :
;

modify VerbRule(AttachToWhat)
    [badness 500] ('attach' | 'connect' | 'tie' | 'affix' ) dobjList
    : 
;

modify VerbRule(Break)
    ('break' | 'ruin' | 'destroy' | 'wreck' | 'smash' | 'demolish' ) dobjList
    :
;

modify VerbRule(Clean)
    ('clean' | 'wipe' | 'polish') dobjList
    : 
;

modify VerbRule(CleanWith)
    ('clean' | 'wipe' | 'polish') dobjList 'with' singleIobj
    : 
;


modify VerbRule(ClimbDown)
    ((('climb' | 'go' | 'walk') 'down') | 'descend') singleDobj
    : 
;

modify VerbRule(ClimbUp)
    ((('climb' | 'go' | 'walk') 'up') | 'ascend') singleDobj
    : 
;

modify VerbRule(Enter)
    ('enter' | 'in' | 'into' | 'in' 'to'
     | ('walk' | 'go' | 'crawl') ('to' | 'in' | 'in' 'to' | 'into'))
    singleDobj
    :
;

modify VerbRule(Feel)
    ('feel' | 'touch' | 'stroke' | 'fondle' ) dobjList
    : 
;

replace VerbRule(GetOffOf)
    'get' ('off' | 'off' 'of' | 'down' 'from') singleDobj
    : GetOffOfAction
    verbPhrase = 'get/getting (off what)'
    askDobjResponseProd = singleNoun
;


modify VerbRule(GoThrough)
    ('walk' | 'go' | 'run' | 'jump' | 'leap' | 'pass' ) ('through' | 'thru')
    singleDobj
    :
;

modify VerbRule(Inventory)
    'i' | 'inventory' | 'take' 'inventory' | 'inv'
    :
;

modify VerbRule(JumpOffI)
    'jump' ('off' | 'down')
    : 
;

VerbRule(JumpDown)
    'jump' 'down' singleDobj
    : JumpDownAction
    verbPhrase = 'jump/jumping (down what)'
    askDobjResponseProd = singleNoun
;

VerbRule(JumpInto)
    'jump' ('in' | 'into') singleDobj
    : JumpIntoAction
    verbPhrase = 'jump/jumping (into what)'
;

DefineTAction(JumpDown)
;

DefineTAction(JumpInto)
;


modify VerbRule(JumpOver)
    ('jump' | 'jump' ('over' | 'across')) singleDobj
    :      
;

modify VerbRule(LookThrough)
    ('look' | 'l' | 'peer' | 'gaze' | 'peep' | 'stare') 
    ('through' | 'thru' | 'out') dobjList
    : 
;

modify VerbRule(Move)
    'move'  dobjList |
    'roll' dobjList ('away' | 'aside' | 'back') |
    'roll' ('away' | 'aside' | 'back' |) dobjList
    :      
;


modify VerbRule(No)
    'no' | 'negative' | 'say' 'no' | 'refuse' | 'no' 'way'
    : 
;

//modify VerbRule(Note)
//     ('note' | '!') singleLiteral
//     :
//;
//
//modify VerbRule(NoteOnly)
//    ('note' | '!')
//    :
//;

modify VerbRule(Pull)
    ('pull' | 'tug' | 'yank') dobjList
    : 
;

modify VerbRule(PutIn)
    ('put' | 'place' | 'insert' | 'drop') dobjList
    ('in' | 'into' | 'in' 'to' | 'inside' | 'inside' 'of') singleIobj
    : 
;



modify VerbRule(Take)
    ('take' | 'pick' ('up'| ) | 'get') dobjList
    | 'pick' dobjList 'up'
    : 
;

modify VerbRule(ThrowTo)
    ('throw' | 'toss' | 'chuck') dobjList 'to' singleIobj | 
    ('throw' | 'toss' | 'chuck') singleIobj dobjList
    : 
;

modify VerbRule(Unfasten)
    ('unfasten' | 'unbuckle' | 'untie') dobjList
    : 
;

modify VerbRule(UnfastenFrom)
    ('unfasten' | 'unbuckle' | 'untie') dobjList 'from' singleIobj
    :     
;

modify VerbRule(Yell)
    'yell' | 'scream' | 'shout' | 'holler' | 'cry' ('out' | )
    :
; 

modify VerbRule(Yes)
    'yes' | 'affirmative' | 'say' 'yes' | 'agree' | 'i' 'do' | 'sure' | 'ok' |
    'okay'
    : 
;


modify grammar firstCommandPhrase(askTellActorTo):
    ('ask' | 'tell' | 'a' | 't' | 'command' | 'order' ) singleNounOnly->actor_
    'to' commandPhrase->cmd_
    :
;

modify grammar actorBadCommandPhrase(main):
    singleNounOnly->actor_ ',' miscWordList
    | ('ask' | 'tell' | 'a' | 't' | 'command' | 'order' ) singleNounOnly->actor_ 
    'to' miscWordList
    :
;

DefineTAction(SmileAt)
;

VerbRule(SmileAt)
    ('smile' | 'grin' | 'beam') 'at' singleDobj
    : SmileAtAction
    verbPhrase = 'smile/smiling (at whom)'
;

VerbRule(SmileAtWhom)
    [badness 500] ('smile' | 'grin' | 'beam') 
    : SmileAtAction
    verbPhrase = 'smile/smiling (at whom)'
    construct()
    {
        /* set up the empty direct object phrase */
        dobjMatch = new EmptyNounPhraseProd();
        dobjMatch.responseProd = singleNoun;
    }
;

DefineTIAction(ThrowInto)
;

VerbRule(ThrowInto)
    ('throw' | 'toss' | 'chuck') dobjList ('in' | 'into' | 'through') singleIobj
    : ThrowIntoAction
    verbPhrase = 'throw/throwing (what) (into what)'
;

DefineTAction(Hug)
;

VerbRule(Hug)
    ('hug' | 'embrace') singleDobj
    | 'give' singleDobj ('a' | ) 'hug'
    : HugAction
    verbPhrase = 'hug/hugging (what)'
;

DefineTIAction(BlockWith)
;

VerbRule(BlockWith)
    ('block' | 'wedge') (|'open') singleDobj (|'open') 'with' singleIobj
    :BlockWithAction
    verbPhrase = 'wedge/wedging (what) (with what)'   
;

VerbRule(BlockWithWhat)
    [badness 500] ('block' | 'wedge') (|'open') singleDobj (|'open')
    :BlockWithAction
    verbPhrase = 'wedge/wedging (what) (with what)'   
    construct()
    {
        /* set up the empty indirect object phrase */
        iobjMatch = new EmptyNounPhraseProd();
        iobjMatch.responseProd = withSingleNoun;
    }
;

DefineTAction(KnockOn)
;

VerbRule(KnockOn)
    ('knock' | 'rap' | 'pound'| 'bang' | 'tap') (| 'on') dobjList
    :KnockOnAction
    verbPhrase = 'knock/knocking (on what)'
;

//DefineTAction(ThrowDir)
//;

modify VerbRule(ThrowDir)
    ('throw' | 'toss' | 'chuck') dobjList ('to' ('the' |) |) singleDir
    :
    verbPhrase = 'throw/throwing (what) (where)'
;

VerbRule(ThrowWhatDir)
    ('throw' | 'toss' | 'chuck')  ('to' ('the' |) |) singleDir
    :ThrowDirAction
    verbPhrase = 'throw/throwing (what) (where)'
    construct()
    {
        /* set up the empty direct object phrase */
        dobjMatch = new EmptyNounPhraseProd();
        dobjMatch.responseProd = singleNoun;
    }
;

DefineIAction(Play)
    execAction()
    {
        if(gActor.isIn(chessRoom))
        {
            switch(emperor.curState)
            {
            case emperorPlaying:
                if(!gActor.isIn(whiteChair) && gActor.posture != sitting)
                    tryImplicitAction(SitOn, whiteChair);
                "To play the emperor at chess, use the commands MOVE PIECE and
                TAKE PIECE WITH PIECE. ";
                break;
            case emperorStone:
                "The game is over. "; break;
            default:
                "There are already two players in this game; your turn may come
                but for now you'll just have to watch. ";
            }
        }
        else
            "{You/he} {have} more important things to do. ";
    }  
;

VerbRule(Play)
    'play' (|'chess'|(|'the')'game')
    :PlayAction
    verbPhrase = 'play/playing'
;

modify PutOnAction
    actionAllowsAll = (!gPlayerChar.isIn(insideShrine))
;



modify YesAction
    execAction
    {
        if(yesNoResponse.response != nil)
            yesNoResponse.execResponse(true);
        else
            inherited;  
    }
;

modify NoAction
    execAction
    {
        if(yesNoResponse.response != nil)
            yesNoResponse.execResponse(nil);
        else
            inherited;  
    }
;

yesNoResponse : object
    response = nil
    responseObj = nil
    execResponse(yes)  
    {    
        responseObj.(response)(yes);    
    }
    reset() { response = nil; responseObj = nil; curFuse = nil; }   
    set(obj, resp)
    {
        /*
         *   The following two lines cover the case where the player types a 
         *   command that triggers a yes-no question two or more times in 
         *   succession.
         */
        if(curFuse != nil)
            curFuse.removeEvent();
        responseObj = obj;
        response = resp;
        curFuse = new Fuse(self, &reset, 1);
    } 
    curFuse = nil
;

DefineSystemAction(Help)
    execSystemAction()
    {        
        helpMenu.display();
        "Done. ";
    }
;

VerbRule(Help)
    'help'
    :HelpAction
    verbPhrase = 'help/helping'
;

/* 
 *   Then some modications to library classes both to deal with the new 
 *   actions above and to add some other functionality.
 */

modify BasicLocation
    thinkHere() { "{You/he} cogitate{s/ed} deeply. "; }   
    prayHere() { "there {is|was} no obvious response. "; }
    travelerArriving(traveler, origin, connector, backConnector)
    {
        travelOrigin = origin;
        inherited(traveler, origin, connector, backConnector);
        enterRoom(traveler);
    }
    enterRoom(traveler) { }
    travelOrigin = nil
    /* 
     *   This property should hold a list of directions and objects for 
     *   which a throw in a certain direction translates into a throw 
     *   into/through that object; e.g.
     *
     *   throwDests = [[northwestDirect, nwArch], [northeastDirection, neArch]]
     */
    throwDests = (location == nil ? [] : location.throwDests)
    throwDest(dirn)
    {
        local res = throwDests.valWhich({x: x[1] == dirn });
        return res == nil ? nil : res[2];
    }
;

//modify OutdoorRoom
//  prayHere = "{your} prayers seem{|ed} to bounce off the sky. "
//;
//
modify Stairway
    dobjFor(Take) maybeRemapTo(gVerbName == 'take', TravelVia, self)
    cannotTakeMsg = &cannotPickUpMsg
    nothingBehindMsg = &cannotLookBehindMsg
;

modify PathPassage
    dobjFor(Take) 
    {
        remap()
        {
            if(gVerbName == 'take')
                return [TravelViaAction, self];
            else
                return nil;
        }
    }
    cannotTakeMsg = &cannotPickUpMsg
    nothingBehindMsg = &unclearWhatBehindMsg
;

modify Component
    nothingBehindMsg = &cannotLookBehindMsg
;



helpMenu: MenuItem
    title = 'Help Menu'
    contents = [ featuresMenu, topInstructionsMenu, helpHintMenu]
;

+ aboutMenu: MenuLongTopicItem
    title = 'About This Game'
    menuContents  { versionInfo.showAbout; }
    menuOrder = 1
;

+ creditsMenu: MenuLongTopicItem
    title = 'Game Credits'
    menuContents  { versionInfo.showCredit; }
;

+ licenseMenu: MenuLongTopicItem
    title = 'License'
    menuContents { LicenseAction.execSystemAction; }
;

modify InstructionsAction
    customVerbs = ['CROSS THE RIVER', 'THINK', 'THINK ABOUT BANANAS', 
        'OPEN DOOR WITH KEY' ]
    allRequiredVerbsDisclosed = true
;

VerbRule(Shoot)
    'shoot' singleDobj
    : ShootAction
    verbPhrase = 'shoot/shooting (what)'
; 

VerbRule(Fire)
    ('fire') singleDobj
    :FireAction
    verbPhrase = 'fire/firing (what) '
;

VerbRule(ShootWith)
    ('fire' | 'shoot') singleIobj 'at' singleDobj |
    'shoot' singleDobj 'with' singleIobj
    :ShootWithAction
    verbPhrase = 'shoot/shooting (what) (with what)'
;

VerbRule(Load)
    'load' singleDobj
    : LoadAction
    verbPhrase = 'load/loading (what)'
;

DefineTAction(Load)
    
;

DefineTAction(Fire)
;

DefineTIAction(ShootWith)
;

DefineTAction(Shoot)
;


modify Thing
    iobjFor(TakeWith)
    {
        verify() { illogical(cannotTakeWithMsg); }
        preCond = [touchObj]
    }
    dobjFor(TakeWith)
    {
        preCond = [touchObj]
        verify() { illogical('{The dobj/he} {cannot} be taken like that. '); }
    }
    dobjFor(Cross)
    {
        preCond = [touchObj]
        verify() { illogical(cannotCrossMsg); }    
    }
    dobjFor(WalkOn)
    {
        preCond = [touchObj]
        verify() { illogical(cannotWalkOnMsg); }
    }
    dobjFor(Swim)
    {
        preCond = [touchObj]
        verify() { illogical(cannotSwimMsg); }
    }
    dobjFor(Answer)
    {
        preCond = [objVisible]
        verify() { illogical(cannotAnswerMsg); }
    }
    dobjFor(FillWith)
    {
        preCond = [objHeld]
        verify() { illogical(cannotFillMsg); }
    }
    iobjFor(FillWith)
    {
        preCond = [objHeld]
        verify() { illogical(cannotFillWithMsg); }
    }
    dobjFor(WriteOn)
    {
        preCond = [touchObj, penHeld, penFull]
        verify() {illogical(cannotWriteOnMsg); }
        action() { "{You/he} {write[s]|wrote} on {the dobj/him}. "; }
    }
    dobjFor(WriteSomethingOn)
    {
        preCond = [touchObj, penHeld, penFull]
        verify() {illogical(cannotWriteOnMsg); }
        action() { "{You/he} {write[s]|wrote} <q><<gLiteral>></q>on
            {the dobj/him}. "; }
    }
    dobjFor(SmileAt)
    {
        preCond = [objVisible]
        verify() { logicalRank(80, 'inanimate'); }
        action { mainReport(futileToSmileAtMsg); }
    }
    dobjFor(ThrowInto)
    {
        preCond = [objHeld]
    }
    iobjFor(ThrowInto)
    {
        preCond = [objVisible]
        verify() { illogical(cannotThrowIntoMsg); }
    }
    iobjFor(ThrowAt)
    {
        /* 
         *   make it more logical to throw at something in easy reach; this 
         *   may help messy avoid messy disambiguation issues,e.g. with 
         *   similar RoomParts in neighbouring locations.
         */
        verify() { if(gActor.canTouch(self)) logicalRank(120, 'nearby'); } 
    }
    
    dobjFor(ThrowDir)
    {
        preCond = [objHeld]
        action()
        {
            local dirn = gAction.dirMatch.dir;
            local obj = gActor.roomLocation.throwDest(dirn);
            if(obj == nil)
            {
                if(dirn != downDirection)
                    "{You/he} {throw[s]|threw} {the dobj/him} <<dirn.name>>
                    and {it dobj/he} land{s/ed} on the ground. ";
                replaceAction(Drop, self);
            }      
            else
                replaceAction(ThrowInto, self, obj);
        }
    }
    
    dobjFor(Hug) 
    {
        preCond = [touchObj]
        verify() { illogical(futileToHugMsg); }
    }
    dobjFor(BlockWith)
    {
        preCond = [touchObj]
        verify() { illogical(cannotBlockMsg); }
    }
    iobjFor(BlockWith)
    {
        preCond = [objHeld]
        verify() { illogical(cannotBlockWithMsg); }
    }
    dobjFor(Examine)
    {
        /* Override the preference for objects being held */
        verify()  {   }
    }
    dobjFor(JumpInto) asDobjFor(Enter)
    dobjFor(JumpDown)
    {
        preCond = [touchObj]
        verify() { illogical(cannotJumpDownMsg); }
    }
    dobjFor(KnockOn)
    {
        preCond = [touchObj]
        action() { mainReport(futileToKnockOnMsg); }
    }
    dobjFor(Fire)
    {
        preCond = [objHeld]
        verify() { illogical(cannotFireMsg); }
    }
    dobjFor(Shoot)
    {
        preCond = [objVisible]
        action() { askForIobj(ShootWith); }
    }
    iobjFor(ShootWith)
    {
        preCond = [objHeld]
        verify() { illogical(cannotShootWithMsg); }
    }
    dobjFor(ShootWith)
    {
        preCond = [objVisible]
    }
    dobjFor(Load)
    {
        preCond = [touchObj]
        verify() { illogical(cannotLoadMsg); }
    }
    dobjFor(PrayTo)
    {
        verify() { illogical(cannotPrayToThatMsg);  }
    }
    cannotBlockMsg = '{That dobj/he} {is} not something {you/he} {can} wedge. ' 
    cannotBlockWithMsg = '{You/he} {can\'t} wedge anything with {that iobj/him}. '
    
    cannotCrossMsg = '{That dobj/he} {is} not something {you/he} {can} cross. '
    cannotWalkOnMsg = '{You/he} {can\'t} walk on {that dobj/him}. '
    cannotSwimMsg  = '{You/he} {can\'t} swim {that dobj/him}. '
    cannotAnswerMsg = '{You/he} {can\'t} answer {that dobj/him}. '
    cannotFillMsg = '{That dobj/he} {is} not something {you/he} {can} fill. '
    cannotFillWithMsg = '{You/he} {can\'t} fill anything with {that dobj/him}. '
    cannotWriteOnMsg = '{That dobj/he} {is} not something {you/he} {can} write
        on. '
    cannotTakeWithMsg = '{You/he} {can\'t} take anything with {that iobj/him}. '
    futileToSmileAtMsg = ('{You/he} ' + smileVerb(gVerbName) +
                          ' inanely at {the dobj/him}. ')
    cannotThrowIntoMsg = '{You/he} {can\'t} throw anything into {the iobj/him}. '
    cannotJumpDownMsg =  '{The dobj/he} {is} not something {you/he} {can} jump
        down. '
    futileToHugMsg = '{You/he} {sees} absolutely no point in hugging
        {that dobj/him}. '
    futileToKnockOnMsg = 'This achieve{s|d} nothing beyond a short tapping sound. '
    cannotShootWithMsg = '{The iobj/he} {is} not something {you/he} {can} shoot
        with. '
    cannotFireMsg = '{You/he} {can\'t} fire {that dobj/him}. '
    cannotPrayToThatMsg = 'Praying to {the dobj/him} would {be|have been} an
        act of superstitious idolatry; the very idea {is|was} beneath you. '
    cannotLoadMsg = '{You/he} {cannot} load {that dobj/him}. '
    
    smileVerb(verb)
    {
        switch(verb)
        {
            case 'grin': return 'grin{s/ned}';
            case 'smile': return 'smile{s/d}';
            default: return verb + '{s/ed}';       
        }      
    }
    
    dobjFor(Hold) asDobjFor(Take)
    isListedInRoomPart(part)
    { 
        if(part == nil)
            return nil;
        if(part.ofKind(Floor))
            return isListed && isNominallyInRoomPart(part);
        return isNominallyInRoomPart(part) && !useSpecialDescInRoomPart(part);
    }  
    
    failCheck(msg) {
        reportFailure(msg);
        exit;
    }
    lakeDrop = 0
    wildernessEast = 0
    wildernessSouth = 0
;

modify Door
    cannotBlockMsg = '{The dobj/he} {is} not something {you/he} need{s/ed} to
        wedge. '
;

modify Decoration
    dobjFor(PrayTo) { verify() { inherited Thing; } }
;


modify Distant
    dobjFor(SmileAt)
    {
        verify() { logicalRank(70, 'inanimate & distant'); }
    }
    dobjFor(PrayTo) { verify() { inherited Thing; } }
;


/* This modification is now covered in the library and is no longer needed */

//modify Hidden
//    discover()
//    {
//        inherited;
//        gSetSeen(self);
//    }
//;
//

modify Actor
    dobjFor(SmileAt)
    {
        verify() { }
        action() {
            if(curState.propDefined(&smileResponse))
                curState.smileResponse;
            else
                smileResponse;
        }
    }
    smileResponse = "<<futileToSmileAtMsg>> {It dobj/he} stare{s/d} stonily
        back. "  
    dobjFor(Hug)
    {
        verify() { }
        action() {
            if(curState.propDefined(&hugResponse))
                curState.hugResponse;
            else
                hugResponse;
        }
    }
    breathe = "\^<<nameIs>> breathing. "
    hugResponse = "{You/he} {take[s]|took} one look at {it dobj/him} and
        {subj actor} {think[s]|thought} better of it. "
    uselessToAttackMsg = 'That would serve no purpose. '
    dobjFor(KnockOn)
    {
        verify() { illogical(cannotKnockOnActorMsg); }
    }
    cannotKnockOnActorMsg = '{The dobj/he} certainly wouldn\'t {|have}
        appreciate{|d} that. '
    dobjFor(PrayTo)
    {
        verify() { }
        check() { failCheck(cannotPrayToThatMsg); }    
    }
    cannotPrayToThatMsg = '{You/he} {do}n\'t believe that praying to
        {the dobj/him} would {be|have been} either appropriate or helpful; it
        would in any case {be|have been} idolatrous. '
    cannotEatMsg = 'You are not a cannibal. '
    tasteDesc = "For as long as {you/he} {can} remember {it actor/he} {have}
        maintained a settled policy of never attempting to taste living
        beings. "
;

modify Container
    iobjFor(ThrowInto) remapTo(PutIn, DirectObject, self)
;

modify BasicChair
    dobjFor(WalkOn) asDobjFor(StandOn)
;

modify Room
    out = perInstance(new OutExitMessage(self))  
;

modify StairwayDown
    cannotJumpDownMsg = 'It would be so much safer just to walk. '
;

class OutExitMessage : NoTravelMessage
    "Which way {do|did} you want to go? <<parent_.cannotGoShowExits(gActor)>> "
    construct(parent) { parent_ = parent; }
    parent_ = nil
;

/* 
 *   change to TakeAction to fix scope problem with TAKE ALL from NestedRooms
 */

modify TakeAction
    /* get the ALL list for the direct object */
    getAllDobj(actor, scopeList)
    {
        local loc;
        
        /* 
         *   Include only objects that are directly in the actor's location, 
         *   or within fixed items in the actor's location.
         */
        loc = actor.location;
        return scopeList.subset(
            {x: x.isDirectlyIn(loc) || x.isInFixedIn(loc) 
            || (loc.ofKind(NestedRoom) && x.isDirectlyIn(loc.location)
                && x != loc)});
    }
;


modify SuggestedTopic
    timesToSuggest = (eventList != nil ? eventList.length : 1) 
;

class SuggestedTalkAboutTopic : SuggestedTellTopic
    fullName = ('talk to {it targetActor/him} about ' + name)
    suggestionGroup = [suggestionTalkAboutGroup]
;

suggestionTalkAboutGroup: SuggestionListGroup
    groupPrefix = "talk to {it targetActor/him} about "
;


class SpecialNounPhraseProd: NounPhraseWithVocab
    /* get the list of objects matching our special phrase */
    getMatchList = []
    
    /* resolve the objects */
    getVocabMatchList(resolver, results, ... )
    {
        /* return all of the in-scope matches */
        return getMatchList().subset({x: resolver.objInScope(x)})
            .mapAll({x: new ResolveInfo(x, 0)});
    }   
    
;

/* 
 *   This modification is probably no longer necessary; the problem it was 
 *   designed to deal with was fixed in TADS 3.0.12
 */

modify RoomPartItem
    showSpecialDesc()
    {
        /* 
         *   if we are in our initial state, show our initial description; 
         *   otherwise, show our special description 
         */
        if(inherited Thing.useInitSpecialDesc() ) 
            //        if (isInInitState && propType(&initSpecialDesc) != TypeNil)
            initSpecialDesc;
        else
            specialDesc;
    }
;

/* 
 *   It probably wasn't really necessary to define a special PreCondition to 
 *   do what penHeld does; new ObjectPrecondition(pen, objHeld) would 
 *   probably do the job perfectly well.
 */

penHeld: PreCondition
    checkPreCondition(obj, allowImplicit)
    {
        /* if the pen is already held, there's nothing we need to do */
        if (pen.isHeldBy(gActor))         
            return nil;
        
        
        /* the pen isn't being held - try an implicit 'take' command */
        if (allowImplicit && pen.tryHolding())
        {
            /* 
             *   we successfully executed the command; check to make sure it 
             *   worked, and if not, abort the command without further 
             *   comment (if the command failed, presumably the command 
             *   showed an explanation as to why) 
             */
            if (!pen.isHeldBy(gActor))
                exit;
            
            
            /* tell the caller we executed an implicit command */
            return true;
        }
        
        /* it's not held and we can't take it - fail */
        reportFailure('You {must} be holding a writing implement before you {can} 
            do that. ');
        exit;
    }
    
;

penFull: PreCondition
    checkPreCondition(obj, allowImplicit)
    {
        /* if the pen is already full, there's nothing we need to do */
        if (pen.full)
        {
            checkIfBroken();
            return nil;
        }
        
        /* the pen isn't being held - try an implicit 'take' command */
        if (allowImplicit && tryImplicitAction(FillWith, pen, inkBottle))
        {
            /* 
             *   we successfully executed the command; check to make sure it 
             *   worked, and if not, abort the command without further 
             *   comment (if the command failed, presumably the command 
             *   showed an explanation as to why) 
             */
            if (!pen.full)
                exit;
            
            checkIfBroken();
            
            /* tell the caller we executed an implicit command */
            
            return true;
        }
        
        /* it's full - fail */
        reportFailure('The pen won\'t write: perhaps it needs some ink.<.reveal
            need-ink> ');
        exit;
    }
    
    checkIfBroken()
    {
        if(pen.broken)
        {
            reportFailure('The pen won\'t write any more; it\'s broken. ');
            exit;
        }
    }
;



DefineTIAction(OpenWith)
;

VerbRule(OpenWith)
    'open' dobjList 'with' singleIobj
    : OpenWithAction
    verbPhrase = 'open/opening (what) (with what)'
;

modify Key
    iobjFor(OpenWith) {
        verify() {} 
        action()
        {
            nestedAction(UnlockWith, gDobj, self);
            nestedAction(Open, gDobj);
        }
    }
    
;

modify SpecialTopic
    matchPreParse(str, procStr)
    {
        if(str.length < 2) return nil;
        return inherited(str, procStr);
    }
;

modify Openable
    dobjFor(OpenWith) 
    {
        verify() { 
            if(isLocked) logicalRank(140, 'locked');      
        }
    }
;

modify NestedRoom
    /*
     *   If restrictedReach is true then the actor must leave this 
     *   NestedRoom to carry out any action on any object that must be 
     *   touchable for that action, unless the object is the NestedRoom, an 
     *   object within the NestedRoom, or one of the explicitly listed 
     *   reachableObjects
     */
    restrictedReach = true
    
    /* 
     *   A list of additional objects that can be reached by an actor from 
     *   within this NestedRoom; the contents of these objects will also be 
     *   reachable.
     */
    
    reachableObjects = []
    
    roomBeforeAction()
    {
        
#ifdef __DEBUG
        /* Allow the special debugging "cheat" actions in any case */ 
        
        if(gActionIn(Pow, Snarf))
            return;
#endif
        
        local obj;
        
        /* 
         *   If we don't want to enforce restricted reach on this NestedRoom 
         *   then there's nothing to do here.
         */
        
        if(!restrictedReach)
            return;
        
        /* 
         *   If all the objects involved in this command are reachable from 
         *   here, or there are no objects involved, then there's nothing 
         *   else to do.
         */   
        
        
        if((gDobj == nil || gDobj==self || gDobj.isIn(self) 
            || reachableObjects.indexOf(gDobj) != nil 
            || reachableObjects.indexWhich({x: gDobj.isIn(x)}) != nil ) 
           && (gIobj == nil || gIobj == self || gIobj.isIn(self)
               || reachableObjects.indexOf(gIobj) != nil 
               || reachableObjects.indexWhich({x: gIobj.isIn(x)}) != nil ))
        {  
            return;
        }
        
        /*
         *   If one of the objects in this command has a touchObj 
         *   precondition, enforce the condition that the actor must first 
         *   leave this NestedRoom before carrying out the action.
         */
        
        if(gIobj != nil && nilToList(gIobj.(gAction.preCondIobjProp)).indexOf(touchObj) != nil)
            obj = gIobj;  
        
        if(gDobj != nil && nilToList(gDobj.(gAction.preCondDobjProp)).indexOf(touchObj) != nil) 
            obj = gDobj;
        
        
        /*
         *   In a further refinement, the obj variable could be used to 
         *   generate a message explaining which object can't be reached. 
         */
        
        if(obj != nil)
        {   
            checkActorOutOfNested(true);        
        }
    }
;

modify Passage
    iobjFor(ThrowInto)
    {
        preCond = [touchObj, objOpen]
        verify() { }
        action()
        {
            mainReport(&throwThroughMsg);
            gDobj.moveInto(destination);            
        }
    }
;

modify SecretDoor
    iobjFor(ThrowInto)
    {
        verify()
        {
            if(!isOpen)
                illogicalNow(cannotThrowIntoMsg);    
        }     
    }
;

modify TravelConnectorLink
    dobjFor(LookThrough)
    {
        verify()
        {
            if(connector && connector.isOpen)
                mainReport(nothingThroughPassageMsg);
            else
                inherited;
        }
    }
    iobjFor(ThrowInto)
    {
        verify() 
        { 
            if(connector == nil)
                inherited;
        }
        action()
        {
            local dest = connector.getDestination(location, gActor);
            if(dest == nil)
            {
                reportFailure(cannotThrowThroughMsg);
                exit;
            }
            if(connector.propDefined(&isOpen) && !connector.isOpen)
            {
                tryImplicitAction(Open, connector);
                if(!connector.isOpen)
                    exit;
            }  
            if(ofKind(EntryPortal) || ofKind(ExitPortal))
                mainReport(&throwThroughMsg);
            else
                mainReport(&throwIntoMsg);
            gDobj.moveInto(dest);              
        }
    }
    cannotThrowThroughMsg = '{You/he} {can\'t} throw anything through {the iobj/him}. '
;

modify RoomPart
    /* 
     *   Make it more likely that EXAMINE RoomPart refers to one nearby
     *
     *   This is probably no longer needed, since the library has now added a
     *   filterResolveList() method to RoomPart to choose the local version 
     *   and avoid pointless disambiguation questions. 
     *
     */
    dobjFor(Examine)
    {
        verify()
        {
            if(!gActor.canTouch(self))
                nonObvious;
            else
                inherited;
        }
    }
    dobjFor(Search)
    {
        preCond = [objVisible, touchObj]
    }
;


VerbRule(TalkImplicit)
    'talk' | 'speak' | 'natter' | 'chat' | 'chatter'
    :TalkImplicitAction
    verbPhrase = 'talk/talking'
;


DefineIAction(TalkImplicit)
    execAction()
    {
        local target = gActor.lastInterlocutor;
        if(target != nil && gActor.canTalkTo(target))
            "{You/he} {are} already talking to <<target.theName>>. ";
        else
            askForDobj(TalkTo);
    }
;

isPortable(obj)
{
    return obj.ofKind(Thing) 
        && !obj.ofKind(NonPortable) 
        && !obj.ofKind(Intangible) 
        && !obj.ofKind(TravelConnector);
}

/*
 *   The ConditionalConnector class is designed for situations in which 
 *   there may at first appear to be only one exit from a location, so that 
 *   one might want to implement out as an unlisted synonym for the 
 *   direction of the apparently sole exit, although subsequently another 
 *   exit may appear, when instead the command OUT should ask the player 
 *   which way to go.     
 */

class ConditionalConnector: TravelConnector 
    /* 
     *   Normally this is is for use on an OUT connector used as a synonym. 
     *   so we don't want to list this exit separately.
     */    
    isConnectorListed = nil
    
    /* The real connector to use when travel is attempted via this one */
    connector = nil
    
    /* 
     *   The condition that must be true for travel via this connector to be 
     *   ambiguous, so that we must ask which way to go instead of actually 
     *   traveling. Typically this will be defined along the lines of
     *
     *   cond = (mySecretExit.isOpen)
     */
    cond = nil
    
    /* The message to be displayed instead of traveling when cond is true */
    
    whichWayMsg = "Which way do you want to go? 
        <<lexicalParent.cannotGoShowExits(gActor)>> "
    
    /* 
     *   The part of the message that is likely to vary from connector to 
     *   connector; this should normally contain a list of possible 
     *   directions of travel, e.g.:
     *
     *   addMsg = ": north or south"
     */
    addMsg = nil
    
    /*
     *   If cond is true, travel via this connector is ambiguous, so ask 
     *   which way to go. Otherwise, travel via the connector for which 
     *   we're effectively an unlisted proxy.
     */
    dobjFor(TravelVia)
    {
        action()
        {
            if(cond)
                whichWayMsg;
            else
                replaceAction(TravelVia, connector);
        }
    }  
;


/*
 *   The purpose of AltListTopic is to provide a non-conversational message 
 *   to the effect that a topic has been exhausted once every item in an 
 *   EventList has been displayed. Note that it only makes sense to use 
 *   AltListTopic in conjunction with an EventList, not with one of its 
 *   subclasses such as StopEventList, and even less with ShuffledEventList 
 *   or RandomEventList.
 *
 *   Note: in more recent works I've dealt with this issue in a different way.
 */

class AltListTopic : AltTopic
    /* You may wish to replace this generic placeholder text */
    topicResponse = "You seem to have exhausted that topic. "
    isConversational = nil
    isActive = (location.ofKind(EventList) 
                && location.curScriptState > location.eventList.length)
;

VerbRule(Features)
    ('interface'|'features')
    : FeaturesAction
    verbRules = 'explain/explaining features'
;

DefineSystemAction(Features)
    execSystemAction()
    {
        featuresMenu.display();
        "Done.";
    }
;

featuresMenu : MenuItem
    title = 'Special Interface Features'
;

+ MenuItem
    title = 'TADS 3 Interface Features'
;

++ MenuLongTopicItem
    title = 'Conversation'
    menuContents = "<i>All Hope Abandon</i> uses the standard conversational
        features built into the TADS3 adv3 library, which build on the standard
        ASK ABOUT/TELL ABOUT system. Since the TADS3 extensions to this system
        may not be familiar, they are explained briefly here.\b
        As in many other games, the basic way to engage in conversation with a
        Non Player Character is to use ASK ABOUT or TELL ABOUT; for example ASK
        BOB ABOUT LIGHTHOUSE or TELL BOB ABOUT THE BLUE BALL. Note that
        repeating the same command may often (although not always) elicit
        further responses to the same topic; you are <i>strongly</i> encouraged
        to try this on any topic of interest in order to get the full story.\b
        If you start a conversation with an explicit greeting command such as
        TALK TO BOB or BOB, HELLO, you'll see that you get a list of possible
        topics you can ask or tell Bob about (which may also include
        suggestions for things to give him, show him, or ask him for). To show
        such a list during the course of the conversation you can issue a
        TOPICS command. Note that the list of suggested topics you see may not
        be exhaustive, and it is generally worth trying other topics that you
        want to pursue even if they're not listed.\b
        To save typing, once a conversation is underway TELL ABOUT and ASK
        ABOUT can be abbreviated to T and A, e.g.\ A LIGHTHOUSE or T BLUE
        BALL.\b
        In this game you can also use the command TALK ABOUT as in TALK TO BOB
        ABOUT THE WEATHER (or, once a conversation is already underway) TALK
        ABOUT THE WEATHER. This actually does exactly the same as TELL BOB
        ABOUT THE WEATHER (and so can also be abbreviated to T WEATHER) but is
        provided for situations where TALK ABOUT may feel a slightly more
        natural command than TELL ABOUT.\b
        The most unfamiliar part of the conversation interface is that which
        allows the player to respond with a command other than the usual ASK
        and TELL or a straightforward YES or NO. This will generally occur when
        the Player Character is required to respond to a question, challenge or
        request from a Non Player Character. At this point, you will be
        presented with a list of possible responses to choose from. For
        example, you might see something like this:\b <q>When did you start
        sleeping with my mistress?</q> Bob demands indignantly.\n (You could
        tell him about your affair, lie, deny ever seeing her, or say it was
        last week)\b Here the first of these is a standard TELL ABOUT response,
        the others are all non-standard. To select the response you want to
        use, type all or part of the text as it appears, e.g.\ you could use
        TELL HIM ABOUT AFFAIR, LIE, DENY, and LAST WEEK to choose each of the
        four options respectively.\b Note that these non-standard options are
        generally valid for the current turn, where they represent possible
        responses to a question that's just been asked, or a demand that's just
        been made. If you choose, say LIE at this point, SAY LAST WEEK will not
        be a valid command on a subsequent turn.\b One exception for this is if
        the NPC is not satisfied with your reply and continues to press you for
        one (e.g.\ you reply TELL BOB ABOUT THE WEATHER at this point and you
        get a response like <q>We were talking about my mistress -- when did
        you start sleeping with her?</q>), in which case the same options will
        still be available. If in doubt you can always use the TOPICS command
        to see whether the special topics are still currently available.\b
        Another point to note about these special responses is that if you
        enter something that doesn't match any of them, the parser will assume
        it's meant to be an ordinary command and will treat it accordingly,
        which will include complaining if what you enter isn\'t a valid
        command.\b One last point: in this game a single-character response
        will NOT be taken as matching a single topic. For example, if one
        possible reponse was I AM GLAD and you enter simply I, this will be
        taken as an INVENTORY command, not as an attempt to choose the I AM
        GLAD topic.\b Finally, you will get more enjoyment from the game if you
        treat NPCs as characters to be conversed with, rather than as puzzles
        to be solved. You don't need to elicit every possible pre-programmed
        response from every NPC in order to win or to score maximum points or
        to understand what's going on (although, as mentioned above, it will
        generally prove worthwhile to elicit more information on key topics by
        ASKing or TELLing about them multiple time). Simply follow lines of
        conversation that seem important or interesting, and you'll discover
        all you need to know without treating the conversation system as a maze
        to be exhaustively mapped.\b (For further information, see the section
        on <q>Interacting with other Characters</q> from the INSTRUCTIONS
        command). "
;

++ MenuLongTopicItem
    title = 'The Status-Line Exits Lister'
    menuContents = "Personally, the list of exits in the status line is a
        feature I really like; it helps players orient themselves and prevents
        room descriptions having to embody a mechanical list of exits. By
        default, therefore, this is a feature that is enabled in this game.\b
        It may be, however, that it&rsquo;s a feature that's not to your taste,
        in which case you can use the following commands to control listing of
        exits:\n
        EXITS ON -- list exits in both the status line and room descriptions.\n
        EXITS OFF -- list exits in neither the status line nor room
        descriptions.\n
        EXITS STATUS -- list exits in the status line only.\n
        EXITS LOOK -- list exits in room descriptions only."
;

++ MenuLongTopicItem
    title = 'The NOTE Command (for transcripts)'
    menuContents = "If you want to make a transcript while playing a TADS 3
        game you can use the SCRIPT command (with SCRIPT OFF ending the
        transcript).\b
        To make notes in the course of the transcript you can use the NOTE
        command, e.g.\b
        <b>>SEARCH SAND</b>\n
        In the sand you find nothing but mpre sand.\b
        <b>>NOTE Typo: mpre </b>\n
        Noted.\b
        If you use this command when you&rsquo;re not actually recording a
        transcript, the parser will advise you of the fact (since it's probably
        pretty pointless to be making notes that are never recorded).\b
        In <<versionInfo.name>> you can also use any punctuation mark or *
        place of NOTE; for example words any of the following can be used to
        insert notes into the transcript:\b
        <b>>!THIS IS A NOTE\b
        >*THIS IS ALSO A NOTE\b
        >[this is also a perfectly good note.\b
        >\\ And so is this.\b
        >/ AND THIS!</b>\b
        This allows you to use whichever of these you like, or even, if you
        want, to use different initial characters for different types of note.
        Do bear in mind, however, that if you or someone else wants to search
        your transcript for your notes, it'll be very helpful if you've been
        consistent in the symbol you've used to mark them. "   
    
;

++ MenuLongTopicItem
    title = 'Disambiguation'
    menuContents = "Every IF player's nightmare is to come up against a
        disambiguation prompt like the following:\b
        &gt;<b>x apple</b>\n
        Which apple do you mean: the apple tree or the apple?\b
        It's not such a problem if it's the tree you want, but the situation
        looks hopeless if it's the fruit you're after. Hopefully, such a
        situation won't arise very often in <i><<versionInfo.name>></i>, but if
        it does, the TADS 3 parser can help you out. All you'd need to do to
        select the apple as opposed to the tree in the about example is to
        reply with:\b
        &gt;<b>second</b>\b
        Indeed, a reply identifying which item you want from a disambiguation
        list by its place in the list (e.g. THE LATTER, LAST, THIRD, THE
        FOURTH) can be used with any TADS disambiguation list. In desperation,
        you can even reply ANY, which might be useful if you were ever
        confronted with something like:\b
        Which apple do you mean: the apple, the apple, the apple or the apple?"
;


+ MenuItem
    title = 'All Hope Abandon Interface Features'
;


++ MenuLongTopicItem
    title = 'Using ALL with verbs (as in EXAMINE ALL)'
    menuContents {
        "Using ALL with verbs like EXAMINE, GIVE TO and EAT can sometimes
        amount to near-cheating, a mimesis-breaking method of provoking the
        game into providing a data-dump on every object in sight, or of
        by-passing a puzzle by using OPEN ALL WITH KEY till you hit on the
        right lock. It does not seem to be a feature that genuinely enhances
        the game-playing experience in the spirit in which the game is intended
        to be experienced, and so this game disallows ALL with all but a small
        group of inventory-handling commands such as TAKE and DROP.<.p>";
        
#ifdef __TESTING
        "However, since your tastes may differ from mine, and I have no wish to
        make my tastes ruin your enjoyment of my game, if you really hate not
        being able to EXAMINE ALL or SHOW ALL TO BOB, then you can use the AALL
        command to enable the more liberal (standard) use of ALL.<.p>";
#endif
    }
;


++ MenuLongTopicItem
    title = 'Non-Standard Commands (Verbs)'
    menuContents {
        "For the most part, this game can be completed using the standard set
        of commands commonly used in Interactive Fiction. But there are a few
        more commands that are either useful or vital:\b
        Firstly, TADS 3 extends the standard conversation system, so you may
        want to read the Conversation chapter.\b
        Various additional <b>System Commands</b>\n";
        
#ifdef __TESTING      
        "AALL -- toggle permitting the use of ALL with most commands.\n ";
#endif      
        
        "EXITS ON -- list exits in both the status line and room
        descriptions.\n
        EXITS OFF -- list exits in neither the status line nor room
        descriptions.\n
        EXITS STATUS -- list exits in the status line only.\n
        EXITS LOOK -- list exits in room descriptions only.\n
        TYPO OF -- disable the typo corrector.\n
        TYPO ON -- enable the typo corrector.\n
        INSTRUCTIONS -- show an instructions menu for general instructions on
        playing IF in TADS 3.\n
        FEATURES -- display this menu, explaining non-standard or interface
        features.\n
        LICENSE -- display the license information.\n
        ROMAN (or GREEK OFF) -- force display of Greek characters in Roman
        alphabet.\n
        GREEK (or GREEK ON) -- restore default mapping of Greek characters.\n   
        GREEK UNICODE -- force the game to use Unicode to display Greek
        characters.\n
        GREEK HTML -- force the game to use HTML to render Greek characters.\n
        NOTE -- add an annotation to a transcript.\n
        RETRY -- take the game back to the last winnable state.\n
        SYMMETRY -- add an extra location to enforce symmetrical exits.\b
        Some additional <b>Game Commands</b>\n
        THINK -- Displays the Player Character's uppermost thoughts in the
        current situation. The command may be abbreviated to TH.\n
        THINK ABOUT X -- Displays what the Player Character thinks about some
        particular topic, e.g.\ THINK ABOUT BANANAS (which may be abbreviated
        to TH BANANAS).\n
        CROSS -- Go across something, e.g.\ CROSS STREAM.\n
        OPEN WITH -- Combined UNLOCK and OPEN action; e.g.\ OPEN DOOR WITH KEY
        first unlocks the door with the key (if it's the right key) and then
        opens the door.\n LOOK NORTH -- describe what you can see to the north.
        You can use this with any valid direction (e.g. LOOK UP, LOOK
        SOUTHWEST) except IN (LOOK IN will be treated as an incomplete LOOK IN
        SOMETHING command). LOOK may be abbreviated to L, and the directions
        may be abbreviated in the normal way, so LOOK NORTHEAST can be entered
        simply as L NE. This command is totally unnecessary for traversing the
        game, but it does give you something else to play with!\b There are a
        few more, but the others are either synonyms for more standard verbs,
        or else inessential actions that a player might try but that have no
        real effect on the game.\n ";   
    }
;

++ helpHintMenu: MenuLongTopicItem
    title = 'Getting Help and Hints'
    menuContents = "The following in-game commands are available for obtaining
        help and hints on playing <i><<versionInfo.name>></i>\b
        HELP -- Overall Help menu, providing menu access to other help
        features.\n
        HINT -- Context sensitive hints to help you out if you're stuck at a
        particular point in the game.\n
        INSTRUCTIONS -- General help with playing Interactive Fiction.\n
        FEATURES -- Description of Interface Features peculiar to this game in
        particular or to TADS 3 games in general.\n
        ABOUT -- General information about this game. "
;

++ MenuLongTopicItem
    title = 'Thinking and Thinking About'
    menuContents = "This game implements two verbs, THINK and THINK ABOUT which
        give you some access to the player character's thoughts.\b
        You can use the command THINK at any point, and the game may respond
        with what the player character thinks about the current situation (or
        may simply display a generic message if there's nothing particular to
        report). This command may be abbreviated to TH.\b
        If you want to know what the player thinks about something (or someone)
        in particular, you can use the THINK ABOUT command, e.g.\ THINK ABOUT
        ARTICHOKES. One of the main purposes of this is to allow the player to
        get at things that the player character knows but the player may not;
        in particular, the player character's expertise as a New Testament
        scholar. Much of this information is not really vital to the game and
        is provided more as background information for the curious. In some
        situations, however, using THINK ABOUT may provide the same kind of
        helpful nudge or clue that you might get from other diagetic (in-game)
        commands such as EXAMINE. Note, however, that THINK ABOUT is <i>not</i>
        intended as a substitute for (or even an adjunct to) the hints system
        (for which you should type HINT).\b
        In case you want to use the THINK ABOUT command a lot, you can
        abbreviate it to TH WHATEVER, e.g.\ THINK ABOUT ARTICHOKES can be
        abbreviated to TH ARTICHOKES. "
    
;

++ MenuLongTopicItem
    title = 'Retry (recovering from sub-optimal endings)'
    menuContents = "Although <i><<versionInfo.name>></i> tries to avoid leaving
        the player in an unwinnable state, there are a couple of places where
        you may arrive at a less than ideal ending, but you don't have a recent
        saved game file, and the stage you'd ideally like to retrace your step
        to is beyond the scope of UNDO (or would require a tedious number of
        UNDOs).\b
        In this situation, RETRY is your friend. If you get to a losing
        end-of-game message and see RETRY among the options, you can select it
        to return the game back to a state where it's guaranteed to be
        winnable, without having to go back to a much earlier saved game file,
        or else having to type repeated UNDOs in the desperate hope that you
        can get back far enough.\b
        By default the RETRY mechanism displays various messages to advise the
        player when it's in effect. If you don't want to see those messages,
        you can hide them with RETRY HIDE. To activate them again use RETRY
        SHOW.\b
        This feature is brought to you courtesy of Michel Nizette's autosave
        library extension. "
;



++ MenuLongTopicItem
    title = 'Greek Characters (or ??????)'
    menuContents = "There are a couple of places in the game where it will try
        to display fragments of Greek (don't worry, the player character will
        translate it for you when you need it translated). If possible, the
        game will try to display it in Greek characters if it thinks your
        system is capable of displaying them, or else use Roman characters if
        thinks it can't. The game makes the best guess it can about this, but
        it may get it wrong, resulting in your seeing a row of question-marks
        (??????? ???? ????? or other unprintable character symbols) or else in
        your seeing Roman letters when you think your system could display
        Greek quite happily.\b
        To get round this, <q><<versionInfo.name>></q> offers a number of
        commands you can use to change the way it display Greek characters:\b
        ROMAN (or GREEK OFF) -- forces display of Greek characters in the Roman
        alphabet.\n
        GREEK (or GREEK ON) -- restores the default mapping of Greek
        characters.\n   
        GREEK UNICODE -- forces the game to use Unicode to display Greek
        characters.\n
        GREEK HTML -- forces the game to use HTML to render Greek characters.\b
        The most likely use of this in practice is for you to issue a ROMAN or
        GREEK OFF command in the event that you see some incomprehensible text
        like <q>????? ????? ??? ????</q>. If the quoted text, <q><<toGreek('en
            arch hn vo logoj')>></q>, appears as something like <q>?? ???? ? ??
        ?????</q>, you need to use the ROMAN command. If the quoted text in the
        previous sentence displayed something like <q>en arche en ho logos</q>
        in Greek or Roman characters then everything's fine and you don't need
        to do anything.\b The GREEK ON (or just GREEK) command is mainly
        provided to allow you to undo the effect of a ROMAN or GREEK OFF
        command, should you issue one mistakenly or experimentally. "
;

++ MenuLongTopicItem
    title = 'Symmetrical Exits'
    menuContents = "There is one point, quite early in the game, when
        travelling WEST from a particular location (let us call it A) will
        involve the player character in a sharp turn to the NORTH (up a slope)
        before arriving at location B. To return from B to A the player
        character must therefore travel SOUTH or DOWN, although attempting to
        travel NORTH or UP from A will not take you to B.\b
        The only way to avoid this asymmetry would be to insert a third
        location C at the point the path bends: then you could travel WEST from
        A to C and EAST from C to A, and NORTH or UP from C to B and SOUTH or
        DOWN from B to C.\b
        <FONT FACE=TADS-TYPEWRITER> \t\tB\n
        \t\t|\n
        \t\t|\n
        \t\tC - - A</FONT>\b
        Most players would probably rather have the asymmetry rather than an
        extra location between A and B whose only function is to turn a corner.
        But if you'd rather have that extra location, you can: just issue the
        command SYMMETRY (which can be abbreviated to SYM), and the additional
        location, allowing fully symmetrical travel, will magically appear. The
        available SYMMETRY commands are:\b
        SYM[METRY] ON -- enable the additional location to enforce symmetrical
        exits.\n
        SYM[METRY] OFF -- disable the additional location and shorten the
        journey.\n
        SYM[METRY]     -- toggle symmetry between on and off.\b
        This is admittedly a feature of pretty marginal interest to most
        players, but you may find it interesting to experiment with. My own
        view is that the game is better without the extra location (the
        default), but it would be interesting to hear from anyone who feels
        otherwise. "
;

++ MenuLongTopicItem
    title = 'Automatic Typo Correction'
    menuContents = "<i><<versionInfo.name>></i> incorporates Steve
        Breslin&rsquo;s Spelling Corrector extension, which can correct
        <i>some</i> typos for you. For example, if you type:\b
        <b>&gt;X PLIOT</b>\b
        Then, depending on context, the game may be able to correct this to:\b
        <b>&gt;X PILOT</b>\b
        And proceed to execute the appropriate command. This can be very useful
        in correcting minor errors automatically, rather than presenting you
        with a parser error and the need to retype a command, but there are two
        things you should note about this feature:\b
        (1) This library extension (rightly) takes a conservative approach to
        typo-correction, making only those corrections that are very likely to
        be correct, rather than risking speculative corrections that were not
        what the player intended. This means that you may often find that the
        game does <i>not</i> correct a typo that looks obvious to you. In such
        a case the reason is probably that the correction was not so obvious to
        the algorithm used by the spelling corrector; it&rsquo;s far better
        <i>not</i> to have the game <q>correct</q> your input to something you
        didn&rsquo;t intend and execute a command you didn&rsquo;t want.\b
        (2) For this reason, no automatic typo correction occurs in
        conversational commands (like ASK and TELL) and other such commands
        where any input is potentially valid. For example, you can ask an NPC
        about anything you like, so it would be highly annoying to have:\b
        <b>&gt;ASK BOB ABOUT LOVE</b>\b
        <q>corrected</q> to:\b
        <b>&gt;ASK BOB ABOUT MOVE</b>\b Which might happen if <q>move</q> but
        not <q>love</q> is one of the words defined in the game&rsquo;s
        dictionary.\b (3) Should you find this typo-correction feature more of
        a hindrance than a help you can disable it with the TYPO command (or
        TYPO OFF). Use another TYPO command (or TYPO ON) to toggle it back on."
    
;

++ MenuLongTopicItem
    title = 'Feedback and Bug Reports'
    menuContents = "If you have any comments on the game, suggestions for
        improvements, or report for bugs, I'd be delighted to receive them at
        <<aHref('mailto:eric.eve@hmc.ox.ac.uk', 'eric.eve@hmc.ox.ac.uk', 'Email
            the author')>>.\b
        If, where appropriate, your bug report (or other comment) could be
        accompanied by a relevant transcript, that would be absolutely
        wonderful, but I appreciate that such a transcript may not always be
        available! "
;




modify playerActionMessages
    needlesslyUnpleasantMsg = 'That would be a needlessly unpleasant act. '
    noReasonToBurnMsg = '{You/he} {have} no reason to burn {the dobj/him}. '
    notElectricalMsg = 'There doesn\'t seem to be any way to do that --
        {it dobj/he} do{es}n\'t seem to be electrically powered. ' 
    cannotMoveThatWayMsg = '{The dobj/he} cannot be moved that way. '
    cannotTakeLocationMsg = ( '{You/he} can\'t take {that/him dobj}
        while {you\'re} ' + gActor.posture.participle + ' {onprep dobj} {it/him dobj} ' )
    cannotKnockOnHoleMsg = '{You/he} {can} hardly knock on {a dobj/him}. '
    nothingThroughPassageMsg = 'If {you/he} really want{s/ed} to see
        what{\'s| was} on the other side of {the dobj/him}, {it actor/he}\'d do
        better to go through {it dobj/him} and take a look. '
    throwThroughMsg = '{The dobj/he} sail{s/ed} through {the iobj/him} and
        land{s/ed} somewhere on the other side. '
    throwIntoMsg = '{You/he} {throw[s]|threw} {the dobj/him} into
        {the iobj/him}, where {it dobj/he} land{s/ed}. '
    
    cannotSeeBehindMsg = '{You/he} {can\'t} see behind {the dobj/him}. '  
    unclearWhatBehindMsg = 'It\'s not clear what would count as
        <q>behind {the dobj/him}</q>. ' 
    
    cannotPickUpMsg = '{You/he} {cannot} pick up {the dobj/him}. '
    
    /* default response to open/close */
    okayOpenMsg = '{You/he} open{s/ed} {the dobj/him}. '
    okayCloseMsg = '{You/he} close{s/d} {the dobj/him}. '
    
    /* default response to lock/unlock */
    okayLockMsg = 'Locked. '
    okayUnlockMsg = 'Unlocked. '  
    
;

/* 
 *   There are one or two places where the game tries to display text in 
 *   Greek characters. This InitObject first tries to determine whether the 
 *   interpreter the game is running on can display Greek characters 
 *   (although there's no waterproof test for this), and then constructs the 
 *   mapping accordingly.
 */

setupGreek: InitObject
    execute()   
    {
        local cs = new CharacterSet(getLocalCharSet(CharsetDisplay)); 
        if(libGlobal.hasGreekHtml == 0)
        {              
            libGlobal.hasGreekHtml = (systemInfo(SysInfoInterpClass) 
                                      != SysInfoIClassText); 
            
            local grAlphabet = '\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9'
                + '\u03BA\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C2'
                + '\u03C3\u03C4\u03C5\u03C6\u03C7\u03C8\u03C9';
            
            libGlobal.hasGreekUnicode = cs.isRoundTripMappable(grAlphabet);
            
            libGlobal.greekLetters = new LookupTable(26,26);
        }
        
        if(libGlobal.hasGreekUnicode)
        {
            libGlobal.greekLetters['a'] = '\u03B1';
            libGlobal.greekLetters['b'] = '\u03B2';
            libGlobal.greekLetters['g'] = '\u03B3';
            libGlobal.greekLetters['d'] = '\u03B4';
            libGlobal.greekLetters['e'] = '\u03B5';
            libGlobal.greekLetters['z'] = '\u03B6';
            libGlobal.greekLetters['h'] = '\u03B7';
            libGlobal.greekLetters['q'] = '\u03B8';
            libGlobal.greekLetters['i'] = '\u03B9';
            libGlobal.greekLetters['k'] = '\u03BA';
            libGlobal.greekLetters['l'] = '\u03BB';
            libGlobal.greekLetters['m'] = '\u03BC';
            libGlobal.greekLetters['n'] = '\u03BD';
            libGlobal.greekLetters['x'] = '\u03BE';
            libGlobal.greekLetters['o'] = '\u03BF';
            libGlobal.greekLetters['p'] = '\u03C0';
            libGlobal.greekLetters['r'] = '\u03C1';
            libGlobal.greekLetters['s'] = '\u03C2';
            libGlobal.greekLetters['j'] = '\u03C3';
            libGlobal.greekLetters['t'] = '\u03C4';
            libGlobal.greekLetters['u'] = '\u03C5';
            libGlobal.greekLetters['f'] = '\u03C6';
            libGlobal.greekLetters['c'] = '\u03C7';
            libGlobal.greekLetters['y'] = '\u03C8';
            libGlobal.greekLetters['w'] = '\u03C9';
            libGlobal.greekLetters['v'] = '';
        }
        else if(libGlobal.hasGreekHtml)
        {
            libGlobal.greekLetters['a'] = '&alpha;';      
            libGlobal.greekLetters['b'] = '&beta;';
            libGlobal.greekLetters['c'] = '&chi;';
            libGlobal.greekLetters['d'] = '&delta;';
            libGlobal.greekLetters['e'] = '&epsilon;';
            libGlobal.greekLetters['f'] = '&phi;';
            libGlobal.greekLetters['g'] = '&gamma;';
            libGlobal.greekLetters['h'] = '&eta;';
            libGlobal.greekLetters['i'] = '&iota;';
            libGlobal.greekLetters['j'] = '&sigmaf;';
            libGlobal.greekLetters['k'] = '&kappa;';
            libGlobal.greekLetters['l'] = '&lambda;';
            libGlobal.greekLetters['m'] = '&mu;';
            libGlobal.greekLetters['n'] = '&nu;';
            libGlobal.greekLetters['o'] = '&omicron;';
            libGlobal.greekLetters['p'] = '&pi;';
            libGlobal.greekLetters['q'] = '&theta;';
            libGlobal.greekLetters['r'] = '&rho;';
            libGlobal.greekLetters['s'] = '&sigma;';
            libGlobal.greekLetters['t'] = '&tau;';
            libGlobal.greekLetters['u'] = '&upsilon;';
            libGlobal.greekLetters['v'] = '';
            libGlobal.greekLetters['w'] = '&omega;';
            libGlobal.greekLetters['x'] = '&xi;';
            libGlobal.greekLetters['y'] = '&psi;';
            libGlobal.greekLetters['z'] = '&zeta;';
            
        }
        else
        {
            
            for(local i = 1; i <= 18; i++ )
            {
                local char = 'abdegiklmnoprstuxz'.substr(i,1);
                libGlobal.greekLetters[char] = char;
            }
            
            
            libGlobal.greekLetters['c'] = 'ch'; //chi
            libGlobal.greekLetters['f'] = 'ph'; //phi       
            libGlobal.greekLetters['y'] = 'ps'; //psi  
            libGlobal.greekLetters['q'] = 'th'; // theta  
            libGlobal.greekLetters['v'] = 'h';  // rough breathing
            libGlobal.greekLetters['j'] = 's';  // terminal sigma
            
            if(cs.isRoundTripMappable('\u00F4\u00EA'))
            {
                libGlobal.greekLetters['h'] = '\u00EA'; //eta
                libGlobal.greekLetters['w'] = '\u00F4'; // omega
            }
            else
            {
                libGlobal.greekLetters['h'] = 'e'; //eta
                libGlobal.greekLetters['w'] = 'o'; // omega
            }
        }     
        
    }
;  

VerbRule(Roman)
    'roman' | 'greek' 'off'
    : RomanAction
    verbPhrase = 'Roman/Romanizing Greek'
;

DefineSystemAction(Roman)
    execSystemAction()
    {
        libGlobal.hasGreekHtml = nil;
        libGlobal.hasGreekUnicode = nil;
        setupGreek.execute();
        "From now on, all Greek characters will be rendered in the Roman
        alphabet. ";
    }
;

VerbRule(Greek)
    'greek' (| 'on'| 'unicode' ->grUnicode| 'html' ->grHtml)
    : GreekAction
    verbPhrase = 'greek/greeking'
;

DefineSystemAction(Greek)
    execSystemAction()
    {
        local grType = nil;
        if(grUnicode != nil)
        {
            libGlobal.hasGreekUnicode = true;
            libGlobal.hasGreekHtml = nil;
            grType = 'unicode'; 
        }
        
        else if(grHtml != nil)
        {
            libGlobal.hasGreekHtml = true;
            libGlobal.hasGreekUnicode = nil;
            grType = 'HTML';
        }
        
        else
        {
            libGlobal.hasGreekHtml = 0;
            grType = 'the original';
        }  
        setupGreek.execute();
        "<q><<versionInfo.name>></q> will now use <<grType>> Greek mapping. ";        
    }
    grUnicode = nil
    grHtml = nil
;

/* 
 *   The toGreek() function makes it easier to define strings of Greek 
 *   characters in the game. For example, one can write toGreek('kai') 
 *   instead of &kappa;&alpha;&iota;. It also takes advantage of the 
 *   character mapping table set up by the setupGreek object defined above 
 *   to display a Greek character string in the most suitable way given the 
 *   capabilities of the interpreter being used.
 */

toGreek(str)
{
    local i, ret = '', strLen = str.length();
    for(i = 1; i <= strLen; i++ )
        ret += greekChar(str.substr(i,1)); 
    return ret;  
}

greekChar(char)
{
    local c = libGlobal.greekLetters[char];
    return c == nil ? char : c;
}


modify libGlobal
    hasGreekHtml = 0
    hasGreekUnicode = 0
    greekLetters = nil
;

modify langMessageBuilder
    paramList_ = static inherited + [['onprep', &actorInPrep, nil, nil, nil]]
    
;


/* Add a custom tag; actually, I'm not sure I ever found a use for this one! */

modify conversationManager
    customTags = 'unreveal'
    doCustomTag(tag, arg) 
    {
        switch(tag)
        {
        case 'unreveal':
            revealedNameTab[arg] = nil;
            break;
        default:
            /* do nothing */
            break;
        }
    }
;

VerbRule(Symmetry)
    ('symmetry' | 'sym') (| 'on'->onOff_ | 'off'->onOff_)
    :SymmetryAction
    verbPhrase = 'toggle/toggling symmetry'
;

DefineSystemAction(Symmetry)
    execSystemAction()
    {
        switch(onOff_)
        {
        case 'on':
            gameMain.symmetry = true; break;
        case 'off':
            gameMain.symmetry = nil; break;
        default:
            gameMain.symmetry = !(gameMain.symmetry);      
        }
        "Exit symmetry is now <<gameMain.symmetry ? 'on' : 'off'>>. ";
        
    }
    onOff_ = nil
;


/* 
 *   This command was made available to beta-testers but was not included in 
 *   the release version. It toggled between allowing and disallowing the 
 *   use of ALL as the object of all commands.
 */

#ifdef __TESTING

VerbRule(AAll)
    'aall'
    : AAllAction
    verbPhrase = 'allow/allowing all'
;

DefineSystemAction(AAll)
    execSystemAction()
    {
        gameMain.allVerbsAllowAll = !gameMain.allVerbsAllowAll;
        "<q>All</q> is now <<gameMain.allVerbsAllowAll ? 'enabled for all'
          : 'disabled for all but basic inventory management'>> commands.
        To toggle this, type AALL again. ";
        
    }
;

#endif



modify libMessages
    
    /* 
     *   The following was needed to avoid an ugly blank line before the 
     *   listing of the special contents of room parts and the bureau 
     *   drawers when multiple objects are being examined.
     *
     *   But this was fixed in a subsequent library version, so this next 
     *   modification is really now redundant. 
     */
    
    announceMultiActionObject(obj, whichObj, action)
    {
        return '\n<.p><.announceObj>' + obj.name
            + ':<./announceObj>&ensp;<.p0>';
    }
    
    /* 
     *   The following message allows for the fact that the game can be 
     *   completed with a variable number of points, depending on the 
     *   precise path chosen.
     */ 
    showScoreMessage(points, maxPoints, turns)
    {
        "In <<turns>> move<<turns == 1 ? '' : 's'>>, you have
        scored <<points>> out of a maximum possible <<maxPoints>> point<<
          maxPoints == 1 ? '' : 's'>> (though the game can be traversed
        with as few as 18 points). ";
    }
    
    /* generic "taste" description of a Thing */
    thingTasteDesc(obj)
    { 
        gMessageParams(obj);
        "{The obj/he} {does}n\'t look like the sort of thing<<obj.isPlural ?
          's' : ''>> {you/he}\'d care to taste. ";
        
    }
;


StringPreParser
    doParsing(str, which)
    {  
        if (which != rmcAskLiteral && rexMatch(notePattern, str) != nil)
        {  
            
            //       return '! "' + str.substr(2).findReplace('"', '\'', ReplaceAll, 1)
            //                 + '"';  
            return '! !';
        }            
        return str;
        
    }
    notePattern = static new RexPattern('^(<space>*<Punct|star>)');

;

//modify playerMessages
//  noMatch(actor, txt)
//     {
//       local trace = t3GetStackTrace() ;
//       local sf = trace.valWhich({x: x.func_ == executeCommand});
//       local verb = '{see}';
//       
//       if(sf != nil)
//       {
//         local toks = sf.argList_[3];
//         if(toks[1][1].toLower is in ('smell', 'sniff'))
//           verb = 'smell{s/ed}';
//         if(toks[1][1].toLower is in ('listen', 'hear'))
//           verb = 'hear{s/d}';
//       }
//       if(verb == '{see}')
//          "{You/he} <<verb>> no <<txt>> here. ";
//       else
//          "{You/he} {are} not aware of any <<txt>> <<tSel('','t')>>here. ";
//     }
// 
/*
 *   I can't recall the point of the following, so I'll comment it out for 
 *   now; Michel Nizette's pasttense extension probably deals with it more 
 *   elegantly in any case.
 */    

//  noMatchForPossessive(actor, owner, txt)
//     {
//       if(gameMain.usePastTense)
//         "That did not seem appropriate. ";
//       else
//         inherited(actor, owner, txt);         
//     }


//; 

#ifdef __DEBUG
VerbRule(Strip)
    'strip' ('off' |)
    : StripAction  
    verbPhrase = 'strip/stripping'
;

DefineIAction(Strip)
    execAction()
    {
        foreach(local cur in gActor.contents)
            if(cur.ofKind(Wearable))
        {
            cur.wornBy = nil;
            cur.moveInto(gActor.location);
            "\^<<cur.theName>> falls straight off. ";
        }
    }
;

DefineIAction(SkipIntro)
    execAction()
    {
        if(gActor.isIn(lectureRoom))
        {
            "Skip rest of intro -- fast forward to...<.p><.reveal q-parallel> ";
            lectureRoom.atmosphereList.curScriptState 
                = lectureRoom.atmosphereList.eventList.length();
        }
        else
            "That command is only available in the lecture room. ";
    }
;

VerbRule(SkipIntro)
    'skip' ('intro' | 'into')
    : SkipIntroAction
    verbRule = 'skip/skipping intro'
;

#endif

