/* Copyright (c) 2000 by Kevin Forchione.  All Rights Reserved. */
/*
 *  TADS ADV.T/STD.T LIBRARY EXTENSION
 *  WALLS.T				
 *  version 1.1
 *
 *      walls.t implements walls, celings, and floors for actor 
 *      locations. Simply #include the module after sense.t and your
 *      rooms will have walls, etc. Rooms with the attribute isOutside
 *      set to true will not have walls or ceilings.
 *
 *----------------------------------------------------------------------
 *  REQUIREMENTS
 *
 *      + HTML TADS 2.5.1 or later
 *      + Requires ADV.T and STD.T
 *      + Requires SENSE.T 3.1
 *      + Should be #included after SENSE.T
 *
 *----------------------------------------------------------------------
 *  IMPORTANT LIBRARY INTERFACE AND MODIFICATION
 *
 *      This module modifies the functioning of the following ADV.T
 *      listing functions:
 *  
 *      Module performs the following process:
 *
 *      1. Define the abstract class
 *          a. Setup instance
 *          b. Check requiresInstance
 *          c. Check for cleanup
 *          d. Clean up
 *      2. Define walls, ceiling, and floor classes
 *      3. Put abstract objects in scope
 *      4. Disambiguate abstract / non-abstract objects
 *      5. Redirect command from abstract to instance
 *      6. Delete the instance
 *
 *      In addition this module modifies the following in Sense.t:
 *
 *          + deepverb validXo()
 *          + deepverb disambigXobj()
 *          + deepverb cantReach()
 *
 *----------------------------------------------------------------------
 *  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
 *
 *      26-Jan-2000 Creation.
 *      30-Jan-2000 Modified creation location of instances.
 *                  Modified floor to move objects to the room instead
 *                  of the floor contents, except for actors sitting or
 *                  lying on the floor.
 */

#define __WALLS_MODULE_

#ifndef __SENSE_MODULE_
#error THIS MODULE REQUIRES THAT YOU #INCLUDE SENSE.T
#endif

#pragma C+

/*======================================================================
 *  A B S T R A C T   C L A S S  
 *====================================================================*/

class abstract: object
    isabstract = true
    /*
     *  Setup an instance of our abstract object. This object is not
     *  considered abstract. It is moved into the actor's location and
     *  the clean-up daemon is started.
     */
    newAbstract(actor) = {
        local obj, loc;
        
        if (self.requiresInstance(actor))
        {
            obj = new self;
            loc = scopeCeiling(actor, nil, &access_reachable);             
            obj.moveInto(loc);
            obj.isabstract = nil;
            notify(obj, &checkForCleanup, 0);
        }
        else
            obj = self;
        return obj;
    }
    /*
     *  Used to check if the actor's location requires an instance of
     *  the abstract. If not then the "You don't see any foo..." message
     *  is displayed.
     */
    requiresInstance(actor) = { return true; }
    /*
     *  Don't delete the object if it is holding things or the actor
     *  location == this object or the object is being referenced by a
     *  command. Because daemons run before endCommand we do not delete
     *  the object if either it or its class is referenced by a command
     *  to allow for endCommand() each_turn style processing.
     */
    checkForCleanup = {
        if (length(self.contents) > 0
        || command.actorPtr.location == self
        || (command.dobjPtr && isclass(self, command.dobjPtr))
        || (command.dobjPtr && self == command.dobjPtr)
        || (command.iobjPtr && isclass(self, command.iobjPtr))
        || (command.iobjPtr && self == command.iobjPtr))
            return;
        else self.cleanup;
    }
    /*
     *  unnotify the daemon for this object, move it into nil (to clean
     *  up the object tree) and then delete it.
     */
    cleanup = {
        unnotify(self, &checkForCleanup);
        self.moveInto(nil);
        delete self;
    }
;

/*======================================================================
 *  W A L L S   A N D   C E I L I N G
 *====================================================================*/

/*
 *  class wall: abstract, decoration
 *
 *  The wall class has been defined as an abstract decoration, which
 *  means that most actions will be regarded as unimportant.
 *
 *  This class defines requires instance to check the actor.location
 *  isOutside attribute. If the attribute is true then we don't require
 *  an instance of wall; otherwise one is dynamically created.
 */
class wall: abstract, decoration
    requiresInstance(actor) = {
        if (actor.location.isOutside) return nil;
        return true;
    }
;

northWall: wall
    adjective = 'north' 'n'
    noun = 'wall'
    sdesc = "north wall"
    isNorthWall = true
;
southWall: wall
    adjective = 'south' 's'
    noun = 'wall'
    sdesc = "south wall"
    isSouthWall = true
;
eastWall: wall
    adjective = 'east' 'e'
    noun = 'wall'
    sdesc = "east wall"
    isEastWall = true
;
westWall: wall
    adjective = 'west' 'w'
    noun = 'wall'
    sdesc = "west wall"
    isWestWall = true
;
neWall: wall
    adjective = 'northeast' 'north-east' 'ne'
    noun = 'wall'
    sdesc = "northeast wall"
    isNeWall = true
;
nwWall: wall
    adjective = 'northwest' 'north-west' 'nw'
    noun = 'wall'
    sdesc = "northwest wall"
    isNwWall = true
;
seWall: wall
    adjective = 'southeast' 'south-east' 'se' 
    noun = 'wall'
    sdesc = "southeast wall"
    isSeWall = true
;
swWall: wall
    adjective = 'southwest' 'south-west' 'sw'
    noun = 'wall'
    sdesc = "southwest wall"
    isSwWall = true
;
ceiling: wall
    noun = 'ceiling'
    sdesc = "ceiling"
    adesc = "the ceiling"
    isCeiling = true
;

/*======================================================================
 *  T H E   F L O O R
 *====================================================================*/

/*
 *  floor: abstract, fixeditem, surface
 *
 *  The floor is treated slightly differently because we can sit on it
 *  and put things on it. The actor doesn't actually walk on the floor,
 *  and it will behave like a nestedroom when they enter it.
 */
floor: abstract, fixeditem, surface
    noun = 'floor' 'ground'
    sdesc = "floor"
    adesc = "the floor"
    isenterable = true
    isFloor = true
    ldesc = {
        "It lies beneath you. "; 
        if (itemcnt(self.location.contents))
        {
            "%You% see%s% <<listcont(self.location)>> here. ";
            listcontcont(self.location);
        }
    }
    ioPutOn(actor, dobj) =
    {
        dobj.doDrop(actor);
    }
    ioPutIn(actor, dobj) =
    {
        dobj.doDrop(actor);
    }
    ioThrowAt(actor, dobj) =
    {
        "Thrown. ";
        dobj.moveInto(actor.location);
    }    
;

/*======================================================================
 *   D E E P V E R B   M O D I F I C A T I O N S    
 *====================================================================*/

modify deepverb
    /* put abstract direct objects into scope */
    replace validDo(actor, obj, seqno) = {  
        command.dobjPtr     = obj;
        
        if (obj.isabstract) return true;

        if (testScope(actor, obj))
            return true;
        else        
            return (testScope(actor, obj, self.dorequires));
    }
    /* put abstract indirect objects into scope */
    replace validIo(actor, obj, seqno) = {  
        command.iobjPtr     = obj;

        if (obj.isabstract) return true;
        
        if (testScope(actor, obj))
            return true;
        else        
            return (testScope(actor, obj, self.iorequires));
    }
	/* if abstract has same vocab remove it */
    disambigDobj(actor, prep, dobj, verprop, wordlist, objlist, flaglist,
                numberWanted, isAmbiguous, silent) = {
        local i, j, len, cnt = 0, abslist = [], newlist = [];
        local vi = [], vj = [];
        
        if (!isAmbiguous) return objlist;
        
        len = length(objlist);

        for (i = 1; i <= len; ++i)
        {
            for (j = i+1; j <= len; ++j)
            {
                vi = [], vj = [];
                if (objlist[i] == objlist[j]) continue;
                vi += getwords(objlist[i], &adjective);
                vi += getwords(objlist[i], &noun);
                vi += getwords(objlist[i], &plural);
                
                vj += getwords(objlist[j], &adjective);
                vj += getwords(objlist[j], &noun);
                vj += getwords(objlist[j], &plural);
                
                if (vi == vj)
                {
                    if (objlist[i] == theFloor)
                        abslist += objlist[i];
                    else if (objlist[j] == theFloor)
                        abslist += objlist[j];
                    else if (objlist[i].isabstract)
                        abslist += objlist[i];
                    else if (objlist[j].isabstract)
                        abslist += objlist[j];
                }
            }
        }
        objlist -= abslist;
        return objlist;
    }
	/* if abstract has same vocab remove it */
    disambigIobj(actor, prep, dobj, verprop, wordlist, objlist, flaglist,
               numberWanted, isAmbiguous, silent) = {
        return self.disambigDobj(actor, prep, dobj, verprop, wordlist, objlist, 
            flaglist, numberWanted, isAmbiguous, silent);
    }
    /* modify to handle command redirection for abstract objects */
	replace cantReach(actor, dolist, iolist, prep) = {		
	    local	i, list, cmdWords;
	    
		if (dolist != nil) {
			list = dolist;
			cmdWords = getCmdPhrase(dolist[1], global.cmdList);
			if (cmdWords == nil)
			    command.cantReachWords = objwords(1);
		    else
		        command.cantReachWords = cmdWords;
		}
		else {
			list = iolist;
			cmdWords = getCmdPhrase(iolist[1], global.cmdList);
			if (cmdWords == nil)
			    command.cantReachWords = objwords(2);
		    else
		        command.cantReachWords = cmdWords;
		}

        if (self.cantSenseActor(actor)) return;
        
        for (i = 1; i <= length(list); ++i)
        {
            if (dolist)
            {
                if (list[i].isabstract)
                    self.cantSenseDoAbstract(actor, list[i], prep);
                else
                    self.cantSenseDoRedirect(actor, list[i], prep);
            }
            else
            {
                if (list[i].isabstract)
                    self.cantSenseIoAbstract(actor, list[i], prep);
                else
                    self.cantSenseIoRedirect(actor, list[i], prep);
            }
        }
	}
	/* create dynamic object in actor's location and redirect command */
	cantSenseDoAbstract(actor, obj, prep) = {
	    local ret;
	    local newobj = obj.newAbstract(actor);
	    if (newobj.isabstract) 
	        self.cantSenseDoRedirect(actor, obj, prep);
	    else
	    {
	        ret = execCommand(actor, self, newobj, prep, command.iobjPtr);
	        command.dobjPtr = obj;
	        if (ret == EC_ABORT) abort;
	        exit;
	    }
	}
	/* create dynamic object in actor's location and redirect command */
	cantSenseIoAbstract(actor, obj, prep) = {
	    local ret;
	    local newobj = obj.newAbstract(actor);
	    if (newobj.isabstract)
	        self.cantSenseIoRedirect(actor, obj, prep);
	    else
	    {
	        ret = execCommand(actor, self, command.dobjPtr, prep, newobj);
	        command.iobjPtr = obj;
    	    if (ret == EC_ABORT) abort;
	        exit;
	    }
	}
;
