#charset "us-ascii"
/*


Tads-3 Container with Lid

Steve Breslin, 2004
email: [email protected]

====

Licence:

Everyone is free to use this, but please provide me with all
improvements and modifications.

====

This extension provides a ContainerWithLid class which you can use to
implement any kind of container which you want to close with a separate
object (the lid). You can make bottles with caps, boxes with lids, etc.

It's quite easy to use this extension: simply include it in your
project, and write an object which inherits from ContainerWithLid; then
include in the container's lidList any object which can "lid" the
container.

*/

#include <adv3.h>
#include <en_us.h>

// example objects:

largeBottle: ContainerWithLid 'large bottle' 'large bottle'
   "It's a large bottle.\n"
   lidList = [largeCap]
   location = me
;

largeCap: Thing 'large cap' 'large cap'
   "It's a large cap.\n"
   location = me
   bulk = 2
;

smallBottle: ContainerWithLid 'small bottle' 'small bottle'
   "It's a small bottle.\n"
   lidList = [smallCap]
   location = me
;

smallCap: Thing 'small cap' 'small cap'
   "It's a small cap.\n"
   location = me
   bulk = 1
;


class ContainerWithLid: ComplexContainer, PreinitObject

   /* A list of objects which can be used to close this object.
    */
   lidList = []

   /* We need to provide our special subContainer and subSurface for
    * each instance of the ContainerWithLid class.
    */
   execute()
   {
       if (subContainer && subContainer.location != self)
       {
           subContainer = subContainer.createInstance();
           subContainer.moveInto(self);
           subContainer.lexicalParent = self;
       }
       if (subSurface && subSurface.location != self)
       {
           subSurface = subSurface.createInstance();
           subSurface.moveInto(self);
           subSurface.lexicalParent = self;
       }
   }

   /* The subSurface is where the lid goes */
   subSurface : ComplexComponent, Surface
   {

       /* This handles putting a lid on the ContainerWithLid object */
       iobjFor(PutOn)
       {
           check()
           {
               /* if there's already something on the surface, we
                * assume it's a lid. So we don't allow another lid.
                */
               if (contents.length) {
                   reportFailure('There\'s already a lid on it.\n');
                   exit;
               }

               /* If the object isn't in our list of acceptable lids,
                * the lid doesn't fit.
                */
               if (!location.lidList.indexOf(gDobj)) {
                   reportFailure('It doesn\'t fit.\n');
                   exit;
               }
               inherited();
           }

           /* place the lid on the surface, and close the container */
           action()
           {
               "You close <<theName>> with <<gDobj.theName>>. ";
               gDobj.moveInto(self);
               location.subContainer.makeOpen(nil);
           }
       }

       /* if an object is removed from the surface, assume it's the
        * lid. with the lid removed, the object is open.
        */
       notifyRemove(obj)
       {
           inherited(obj);
           location.subContainer.makeOpen(true);
       }
   }

   /* The subContainer is where the contents of the object go,
    * excepting the lid of course.
    */
   subContainer : ComplexComponent, OpenableContainer
   {

       /* When someone tries simply to close the object, we redirect
        * processing, asking "what (lid) do you want to put on the
        * container?"
        */
       dobjFor(Close)
       {
           action()
           {
               PutOnAction.retryWithMissingDobj(gAction, ResolveAsker);
           }
       }

       /* We don't actually want the lid to go on the subContainer,
        * but on the subSurface.
        */
       iobjFor(PutOn) remapTo(PutOn, DirectObject, location.subSurface)

       /* Opening the container means taking the cap from the
        * container.
        */
       dobjFor(Open)
       {
           action()
           {
               if (location.subSurface.contents)
               {
                   "You remove
                   <<location.subSurface.contents[1].theName>> from
                   <<theName>>.\n";
                   replaceAction(Take, location.subSurface.contents[1]);
               }
           }
       }

       /* We're initially open if there's no lid on the subSurface. */
       initiallyOpen()
       {
           return (location.subSurface.contents == []);
       }

       /* Only things smaller than the lid can fit through the
        * container's mouth.
        */
       canFitObjThruOpening(obj)
       {
           return (location.lidList[1].getBulk == 0
                   || obj.getBulk < location.lidList[1].getBulk);
       }
   }
;

/* treat "cap X with Y" and "close X with Y" the same as "put Y on X"
*/
VerbRule(CapWith)
   ('cap' | 'close') singleIobj 'with' singleDobj
   : PutOnAction
   verbPhrase = 'cap/capping (what) (with what)'
;

/* treat "cap X" the same as "close X" */
VerbRule(Cap)
   'cap' singleDobj
   : CloseAction
   verbPhrase = 'cap/capping (what)'
;