#charset "us-ascii"
/*


Tads-3 Container with Lid

Steve Breslin, 2004
email: versim@hotmail.com

====

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)'
;

