/* Copyright (c) 1999, 2000 by Kevin Forchione.  All Rights Reserved. */
/*
*  TADS ADV.T/STD.T LIBRARY EXTENSION
*  DOORS.T
*  version 1.0
*
*      This module implements a floatingItem object that behaves like
*      two TADS doorways. This implementation is easier to work with
*      and modify than the earlier doorItem class.
*
*----------------------------------------------------------------------
*  REQUIREMENTS
*
*      + HTML TADS 2.5.0 or later
*      + Requires ADV.T and STD.T
*      + Should be #included after ADV.T and STD.T.
*
*----------------------------------------------------------------------
*  IMPORTANT LIBRARY INTERFACE AND MODIFICATION
*
*      None.
*
*----------------------------------------------------------------------
*  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
*
*          23-Oct-99:  Creation.
*/

#define __DOORS_MODULE_


#pragma C+

/*
*  door: floatingItem, fixeditem, obstacle
*
*  A door is an obstacle that impedes progress when it is closed.
*  Unlike the doorway, a door is a floatingItem and should not have its
*  location attribute coded. Instead, use the foundin list to indicate
*  the rooms in which the door is to be found.
*
*  To make a simple two-sided door you need only code both room
*  objects in the foundin list of the door:
*
*      foundin = [ hallway, kitchen ]
*
*  To make a one-sided door, code the foundin list with the room
*  object in which the door is to be found, and the doorto list
*  with the room object to which the door leads:
*
*      foundin = hallway
*      doorto = kitchen
*
*  If you want a door that returns rooms different from those it is
*  found in, simply code both the foundin list and doorto list
*  accordingly, each element of the doorto list must have a
*  corresponding foundin element:
*
*      foundin = [ hallway, kitchen ]
*      doorto = [ foyer, hallway ]
*
*  From the hallway the door leads to the foyer, but from the kitchen
*  it leads to the hallway.
*/
class door: doorway, floatingItem
   foundin = []
   doorto = nil
   location =
   {
       local f, actor, loc, dloc, foundList;

       if (self.foundin == nil)
           return nil;

       actor = parserGetObj(PO_ACTOR);
       if (actor == nil)
           actor = parserGetMe();

       loc = actor;
       while(loc)
       {
           dloc = loc;
           loc = loc.location;
       }

       //
       //  convert self.foundin into a list
       //
       switch(proptype(self, &foundin))
       {
           case DTY_NIL:
               foundList = [];
               break;
           case DTY_OBJECT:
               foundList = [ self.foundin ];
               break;
           case DTY_LIST:
               foundList = self.foundin;
               break;
           default:
               return nil;
       }

       if (find(foundList, dloc))
           return dloc;
       else
           return nil;
   }
   findDest(actor) =
   {
       local f, loc, foundList, destList;

       loc = self.location;

       //
       //  convert self.foundin into a list
       //
       switch(proptype(self, &foundin))
       {
           case DTY_NIL:
               foundList = [];
               break;
           case DTY_OBJECT:
               foundList = [ self.foundin ];
               break;
           case DTY_LIST:
               foundList = self.foundin;
               break;
           default:
               return nil;
       }

       //
       //  convert self.doorto into a list
       //
       switch(proptype(self, &doorto))
       {
           case DTY_NIL:
               destList = [];
               break;
           case DTY_OBJECT:
               destList = [ self.doorto ];
               break;
           case DTY_LIST:
               destList = self.doorto;
               break;
           default:
               return nil;
       }

       //
       //  door is linked to room directions, but no foundin
       //  list exists. Simply return the noexit message.
       //
       if (length(foundList) == 0)
       {
           return self.noexit;
       }

       //
       //  We don't have a doorto list. Use the foundin list,
       //  which must have 2 elements.
       //
       if (length(destList) == 0)
       {
           if (length(foundList) != 2)
           {
               "\n[TADS-DOORS-001] When omitting the
               doorto list, the foundin list should
               contain 2 elements.\n";
               return nil;
           }
           if (loc == foundList[1])
               return foundList[2];
           else
               return foundList[1];
       }

       //
       //  We have a doorto list, each element should correspond to
       //  an element in the foundin list.
       //
       if (length(foundList) != length(destList))
       {
           "\n[TADS-DOORS-002] <<self.thedesc>> requires a
           corresponding doorto element for each foundin
           element.\n";
           return nil;
       }

       //
       //  Search for the actor location in the foundin list.
       //
       f = find(foundList, loc);

       //
       //  Door isn't in the actor location. return noexit.
       //
       if (f == nil)
       {
           return self.noexit;
       }

       //
       //  return the corresponding doorto element
       //
       return destList[f];
   }
   destination =
   {
       local actor = parserGetObj(PO_ACTOR);

       if (actor == nil)
           actor = parserGetMe();

       //
       //  The door location is nil or the actor's location doesn't
       //  match any object in the foundin list
       //
       if (self.location == nil)
           return self.noexit;

       if (self.isopen)
           return self.findDest(actor);
       else if (!self.islocked
           && !self.noAutoOpen)
       {
           self.setIsopen(true);
           "(Opening << self.thedesc >>)\n";
           return self.findDest(actor);
       }
       else
       {
           "%You%'ll have to open << self.thedesc >> first. ";
           setit(self);
           return nil;
       }
   }
   setIsopen(setting) =
   {
       /* update my status */
       self.isopen = setting;
   }
   setIslocked(setting) =
   {
       /* update my status */
       self.islocked = setting;
   }
   noexit = { "%You% can't go that way. "; return nil; }
;

/*
*  lockableDoor: door
*
*  This is just a normal door with the islockable and
*  islocked properties set to true.  Fill in the other
*  properties (foundin and doorto) as usual.  If
*  the door has a key, set property mykey to the key object.
*/
class lockableDoor: door
   islockable = true
   islocked = true
;

#pragma C-