/* Copyright (c) 2000 by Kevin Forchione.  All Rights Reserved. */
/*
 *  TADS ADV.T/STD.T LIBRARY EXTENSION
 *  COMPASS.T				
 *  version 1.0
 *
 *	    compass.t simulates the Inform compass. Directions are objects
 *      and also do double duty as walls, floor, and ceiling. Like
 *      the Inform compass this one can define WITHOUT_DIRECTIONS in 
 *      the game source, which will remove the usual directions from
 *      the compass object.
 *
 *----------------------------------------------------------------------
 *  REQUIREMENTS
 *
 *      + HTML TADS 2.4.0 or later
 *      + Should be #included after ADV.T and STD.T 
 *
 *----------------------------------------------------------------------
 *  IMPORTANT LIBRARY INTERFACE AND MODIFICATION
 *
 *      + Replaces checkReach() function.
 *      + Modifies deepverb validXoList() and validXo() methods.
 *      + Modifies inspectVerb validDo() method.
 *      + Replaces travelVerb definitions.
 *      + Defines parseErrorParam() method.
 *
 *----------------------------------------------------------------------
 *  COPYRIGHT NOTICE
 *
 *  	You may modify and use this file in any way you want, provided that
 *		if you redistribute modified copies of this file in source form, the
 *   	copies must include the original copyright notice (including this
 *   	paragraph), and must be clearly marked as modified from the original
 *   	version.
 *
 *------------------------------------------------------------------------------
 *  REVISION HISTORY
 *
 *		20-Jun-00:	Creation.
 */
 
#ifndef __COMPASS_MODULE_
#define __COMPASS_MODULE_

#pragma C+

/*----------------------------------------------------------------------
 *  THE COMPASS
 *--------------------------------------------------------------------*/
 
class CompassDirection: decoration
    ldesc = "%You% see nothing special about <<self.thedesc>>."
    verDoGo(actor) = {}
    doGo(actor) = {actor.travelTo(self.travelDir(actor));}
;

class SpecialCompassDirection: CompassDirection
    dobjGen(a, v, i, p) = {
        if (v != goVerb) {
            "%You% can't see any such thing. ";
            exit;
        }
    }
    iobjGen(a, v, d, p) = {self.dobjGen(a, v, d, p);}
;    

compass: object
    sdesc = "compass"
;

#ifndef WITHOUT_DIRECTIONS

nObj: CompassDirection
    location = compass
    sdesc = "north wall"
    noun = 'wall'    
    adjective = 'n' 'north'
    travelDir(actor) = {return actor.location.north;}
;
sObj: CompassDirection
    location = compass
    sdesc = "south wall"
    adjective = 's' 'south' 
    noun = 'wall'
    travelDir(actor) = {return actor.location.south;}
;
eObj: CompassDirection
    location = compass
    sdesc = "east wall"
    adjective = 'e' 'east' 
    noun = 'wall'
    travelDir(actor) = {return actor.location.east;}
;
wObj: CompassDirection
    location = compass
    sdesc = "west wall"
    adjective = 'w' 'west' 
    noun = 'wall'    
    travelDir(actor) = {return actor.location.west;}
;
neObj: CompassDirection
    location = compass
    sdesc = "northeast wall"
    adjective = 'ne' 'northeast' 
    noun = 'wall'
    travelDir(actor) = {return actor.location.ne;}
;
nwObj: CompassDirection
    location = compass
    sdesc = "northwest wall"
    adjective = 'nw' 'northwest' 
    noun = 'wall'
    travelDir(actor) = {return actor.location.nw;}
;
seObj: CompassDirection
    location = compass
    sdesc = "southeast wall"
    adjective = 'se' 'southeast' 
    noun = 'wall'
    travelDir(actor) = {return actor.location.se;}
;
swObj: CompassDirection
    location = compass
    sdesc = "southwest wall"
    adjective = 'sw' 'southwest' 
    noun = 'wall'
    travelDir(actor) = {return actor.location.sw;}
;
uObj: CompassDirection
    location = compass
    sdesc = "ceiling"
    noun = 'u' 'up' 'ceiling'
    travelDir(actor) = {return actor.location.up;}
;
dObj: CompassDirection
    location = compass
    sdesc = "floor"
    noun = 'd' 'down'
    travelDir(actor) = {return actor.location.down;}
;

#endif /* WITHOUT_DIRECTIONS */

outObj: SpecialCompassDirection
    location = compass
    sdesc = "outside"
    noun = 'out' 'outside'
    travelDir(actor) = {return actor.location.out;}
;
inObj: SpecialCompassDirection
    location = compass
    sdesc = "inside"
    noun = 'in' 'inside'
    travelDir(actor) = {return actor.location.in;}
;

/*----------------------------------------------------------------------
 *  CHECK REACH MODIFICATIONS
 *
 *  These modifications always add the compass contents to scope.
 *--------------------------------------------------------------------*/
replace checkReach: function(loc, actor, v, obj)
{
    if (obj == numObj || obj == strObj || obj.isIn(compass))
        return;
    if (!(actor.isCarrying(obj) || obj.isIn(actor.location)))
    {
        if (find(loc.reachable, obj) != nil)
             return;
        "%You% can't reach "; obj.thedesc; " from "; loc.thedesc; ". ";
        exit;
    }
}


/*----------------------------------------------------------------------
 *  DEEP VERB MODIFICATIONS
 *
 *  These modifications always add the compass contents to scope.
 *--------------------------------------------------------------------*/
 
modify deepverb                // A deep-structure verb.
    validDo(actor, obj, seqno) =
    {
        if (obj.isIn(compass)) return true;
        return obj.isReachable(actor);
    }
    validDoList(actor, prep, iobj) =
    {
        local ret;
        local loc;
        
        loc = actor.location;
        while (loc.location)
            loc = loc.location;
        ret = visibleList(actor, actor) + visibleList(loc, actor)
               + global.floatingList + compass.contents;;
        return ret;
    }
    validIo(actor, obj, seqno) =
    {
        if (obj.isIn(compass)) return true;
        return obj.isReachable(actor);
    }
;
modify inspectVerb
    validDo(actor, obj, seqno) =
    {
        if (obj.isIn(compass)) return true;
        return obj.isVisible(actor);
    }
;

/*----------------------------------------------------------------------
 *  TRAVEL VERB MODIFICATIONS
 *
 *  (a) goVerb defines doAction = 'Go' for verb templates verDoGo() and 
 *      doGo() defined by the compass direction objects.
 *  (b) Replace travelVerbs so that they redirect travel to the goVerb.
 *--------------------------------------------------------------------*/

goVerb: travelVerb
    verb = 'go'
    doAction = 'Go'
    /*
     *  Allowing multiple objects can cause erroneous sdescs
     *  (e.g. nObj.sdesc) to get printed.
     */
    rejectMultiDobj(prep) = {
        "You can't use multiple objects with this verb.";
        return true;
    }
    /*
     *  Beside returning the compass contents as default objects this
     *  method sets the important global.parseErrDirections flag used
     *  by parseErrorParam().
     */
    doDefault(actor, prep, io) = {
        global.parseErrDirections = true;
        return compass.contents;
    }
    sdesc = "go"
;

replace eVerb: travelVerb
    verb = 'e' 'east'
    sdesc = "go east"
    action(actor) = {parserReplaceCommand('go east');}
;
replace sVerb: travelVerb
    verb = 's' 'south'
    sdesc = "go south"
    action(actor) = {parserReplaceCommand('go south');}
;
replace nVerb: travelVerb
    verb = 'n' 'north'
    sdesc = "go north"
    action(actor) = {parserReplaceCommand('go north');}
;
replace wVerb: travelVerb
    verb = 'w' 'west'
    sdesc = "go west"
    action(actor) = {parserReplaceCommand('go west');}
;
replace neVerb: travelVerb
    verb = 'ne' 'northeast'
    sdesc = "go northeast"
    action(actor) = {parserReplaceCommand('go ne');}
;
replace nwVerb: travelVerb
    verb = 'nw' 'northwest'
    sdesc = "go northwest"
    action(actor) = {parserReplaceCommand('go nw');}
;
replace seVerb: travelVerb
    verb = 'se' 'southeast'
    sdesc = "go southeast"
    action(actor) = {parserReplaceCommand('go se');}
;
replace swVerb: travelVerb
    verb = 'sw' 'southwest'
    sdesc = "go southwest"
    action(actor) = {parserReplaceCommand('go sw');}
;
replace inVerb: travelVerb
    verb = 'in' 'enter' 'go to' 'go into'
    sdesc = "enter"
    doAction = 'Enter'
    ioAction(onPrep) = 'EnterOn'
    ioAction(inPrep) = 'EnterIn'
    ioAction(withPrep) = 'EnterWith'
    action(actor) = {parserReplaceCommand('go in');}
;
replace outVerb: travelVerb
    sdesc = "go out"
    action(actor) = { actor.travelTo(self.travelDir(actor)); }
    verb = 'out' 'go out' 'exit' 'leave'
    travelDir(actor) = { return actor.location.out; }
;
replace dVerb: travelVerb
    verb = 'd' 'down'
    sdesc = "go down"
    action(actor) = {parserReplaceCommand('go down');}
;
replace uVerb: travelVerb
    verb = 'u' 'up'
    sdesc = "go up"
    action(actor) = {parserReplaceCommand('go up');}
;

/*----------------------------------------------------------------------
 *  ERROR MESSAGE HANDLING FOR <<GO>>
 *--------------------------------------------------------------------*/

parseErrorParam: function(errnum, str, ...) {
    if (errnum == 140 && global.parseErrDirections) {
        global.parseErrDirections = nil;
        return 'Where do you want to ';
    }
    else return nil;
}

#pragma C-

#endif /* __COMPASS_MODULE_ */
