! --------------------------------------------------------------------
!
! RECEPTACLES
! ===========
! (containers with defined capacities)
! for use with INFORM 6.x, (c) 1999 Graham Nelson
! Version 1.0 (2002-July-06)
!
! Written & copyright (c) 2002, 2006 by Peer Schaefer.
!
! The distribution of this file for free or for profit is allowed, as
! long as this copyright notice and the disclaimer (see below) remain
! intact. You may distribute modified versions of this file under the
! same terms, as long as you place a notice on top of the file that it
! is modified by you, and provide your name and the date of the
! modifications. The unmodified or modified version distributed by you
! must be (re)distributable, modifyable and usable under the same terms
! as this version.
!
! You may use this library unaltered or in a modified version freely
! for your own games, commercial or not. It would be nice if you give
! me some credit or provide the sourcecode, but that is not legally
! required.
!
! NO WARRANTY, NO LIABILITY. PROVIDED FREE OF CHARGE "AS IS". USE ON
! YOUR OWN RISK!
!
! Bug reports, comments and suggestions to: [email protected]
!
! The latest version should be available at:
! http://www.wolldingwacht.de/if/recept.html
!
! --------------------------------------------------------------------
!
!
! REFERENCE
! =========
! This library contribution implements a new class of objects called
! "receptacles". Receptacles are containers (or supporters) that are
! aware of weight, volume and size. Every time the player tries to put
! an object into such a receptacle the receptacle checks whether its
! remaining storage-capacity is sufficient or not.
!
! To define its weight, volume and size every object in the game can
! (but hasn't to) provide the properties
!   - weight
!   - volume
!   - size   ("size" represents the maximum length on any axis, e.g.
!             the length of an arrow or of a staff. It's obvious that
!             the volume of a quiver is "used up" when you store many
!             arrows in it, but the length of the quiver is not "used
!             up" by the length of the stored arrows: it only
!             indicates whether an arrow is too long for the quiver
!             or not.)
!
! Each of these three properties can be a numerical value or a
! routine that returns a numerical value. If one of these properties
! is missing or has the value 0 (zero), the respective dimension of
! the object is 0 (that means that the object has no or only
! neglectable weight, volume or size).
!
! Please notice: Weight, volume and size are measured in abstract
!                "units" (plain numbers). Whether one "unit" of e.g.
!                weight is a grain, a british pound, a kilogram, an
!                US-ton or 1 sun-mass is completely up to you and
!                your program.
!
! To create a container which automatically checks weight, volume and
! size use the new "receptacle"-class. Containers of this class can
! provide three new properties:
!   - capacity_weight
!   - capacity_volume
!   - capacity_size
! Each of these three properties can be a numerical value or a routine
! that returns a numerical value. If one of these properties is missing
! or has the value INFINITE_CAPACITY (a predefined constant), the
! respective capacity of the receptacle is infinite.
!
! Of course containers/receptacles can also provide weight, volume and
! size (since they are not only containers/receptacles but also objects
! that can be stored inside other containers/receptacles).
!
! For clarification:
!  * The player can store multiple objects in a receptacle, as long as
!    their total-weight does not exceed the capacity_weight of the
!    receptacle.
!  * The player can store multiple objects in a receptacle, as long as
!    their total-volume does not exceed the capacity_volume of the
!    receptacle.
!  * In other words: capacity_weight and capacity_volume are "used up"
!    by objects stored inside the receptacle.
!  * capacity_size is a different story: it's not "used up" but
!    checked seperately for each object.
!
! As a little extra a receptacle can also provide the new property
! capacity_number. That property can be a number or a routine that
! returns a number. It indicates how many objects (maximum) can be
! stored inside that receptacle. Any objects beyond this maximum are
! rejected.
!
!
! Some technical details:
! -----------------------
!  * Calculating the weight of a container (or supporter) is a
!    recursive process: The weights of all (immediate) child-objects
!    are added to the weight of the container itself; the total-
!    weights of these child-objects are calculated by adding up the
!    weights of THEIR child-objects; and so forth.
!  * If the weight of an object is provided as a routine, this routine
!    should return the TOTAL weight of the object (including the
!    weight of all child-objects inside, grand-child-objects etc).
!    The weights of child-objects are NOT added automatically in this
!    case. That gives you more flexibility in the creation of special
!    containers (e.g. magical bags that weigh less than their
!    contents).
!  * Volume and size of childrens are NOT added to the objects
!    volume/size (assuming that standard-containers are not flexible
!    and have fixed proportions). To override this you can provide
!    your own VOLUME and SIZE properties, which calculate volume resp.
!    size at run-time (see example below).
!  * It's totaly legal to create a container whose volume_capacity is
!    greater than it's volume (it's pretty inplausible, but this way
!    you can create magical bags, black holes and other weird things).
!
!
! DEBUGGING VERBS:
! ----------------
! When compiled in debug-mode recept.h provides three meta-verbs:
!   $weigh OBJECT           - prints the weight of the object
!   $measure OBJECT         - prints all dimensions of an object
!   $capacity OBJECT        - prints the capacities of a receptacle
!
!
!
! EXAMPLE 1:
! ----------
! Receptacle box "box" with name "box",
!        volume 10,                ! The box itself has a volume of 10
!        capacity_volume 9,        ! And it can store a volume of 9
!        has container;
!
! Object stone "stone" with name "stone",
!        volume 2;                 ! This stone has a volume of 2
!
! The lines above create a box with a volume of 10 and a capacity of 9,
! and a stone with a volume of 2. The player can put up to 4 stones in
! the box (with a total volume of 4x2=8). A fifth stone can't be stored
! in the box, since the capacity of the box is 9 and the total volume
! of 5 stones would be 10.
!
!
! EXAMPLE 2:
! ----------
! The following example creates a wooden box and a steel box:
!
! Receptacle wooden_box "wooden box"
!        with name "wooden" "box",
!        volume 10,              ! The box itself has a volume of 10
!                                ! units
!        capacity_volume 9,      ! And it can store objects up to a
!                                ! volume of 9 units
!        has container;
!
! Receptacle steel_box "steel box"
!        with name "steel" "box",
!        volume 8,               ! The box itself has a volume of 8
!                                ! units
!        capacity_volume 7,      ! And it can store objects up to a
!                                ! volume of 7 units
!        has container;
!
! You can put the steel box into the wooden box (volume 8 fits in
! capacity 9) but not the wooden box into the steel box (volume 10
! doesn't fit in capacity 7). If you put something with a volume of 2
! or greater into the wooden box, you can't put the steel box into
! it because that would require a free volume of 8 or more.
!
!
! EXAMPLE 3:
! ----------
! Volume and size of childrens are NOT added to the objects volume or
! size (assuming that containers are not flexible and have fixed
! proportions), so if you want to create a flexible bag whose volume
! grows if objects are stored inside, you should code an appropriate
! routine as the volume property:
!
! Receptacle -> bag "flexible bag"
!        with name "flexible" "bag",
!        capacity_volume 20,
!        capacity_size 5,
!        volume [ v i;
!                v = 1;                         ! Minimal volume.
!                objectloop (i in bag)          ! Add volumes of all
!                        v = v + VolumeOf (i);  ! immediate childs.
!                return v;
!        ],
!        size [ s i;
!                s = 1;                  ! Minimal size when empty.
!                objectloop (i in bag)   ! Find the largest child:
!                        if (SizeOf (i) > s) s = SizeOf (i);
!                return s;               ! (The size of the largest
!        ],                              ! immediate child determines
!        has container;                  ! the size of the bag.)
!
!
! EXAMPLE 4:
! ----------
! The weights of childrens are added automatically to the objects
! weight. You can override this by providing an own weight-routine.
! The following example creates a wonder-bag, whose total-weight is
! only half the total-weight of it's contents:
!
! Receptacle -> wonder_bag "wonder bag"
!        with name "wonder" "magic" "bag",
!        capacity_volume 100,
!        weight [ w i;
!                w = 1;                        ! Base weight of bag is 1
!                objectloop (i in wonder_bag)
!                        w = w + WeightOf (i); ! Add up weights...
!                return (w/2);                 ! ...and return 50%
!        ],
!        has container;
!
!
! CONTACT:
! --------
! Bug reports, suggestions, questions, comments and congratulations to:
! [email protected]
! (or, if that fails, to: [email protected])




System_file;                      ! to avoid a warning for not using the
                                 ! __DUMMY_RECEPT class


Class __DUMMY_RECEPT              ! Defines the seven new properties without
       with                      ! using up any program-space or
               weight 0,         ! runtime-memory
               size   0,
               volume 0,
               capacity_weight 0,
               capacity_size   0,
               capacity_volume 0,
               capacity_number 0;


Constant INFINITE_CAPACITY -1;    ! (-1) symbolizes an infinite capacity


! --------------------------------------------------------

! The following functions calculate the weight, volume and size
! of any given object.

[ WeightOf obj w i;
       if (obj provides weight)
       {
               if (metaclass(obj.weight) == Routine)
                       return indirect (obj.weight);
               w = obj.weight;
       } else  w = 0;
       if ((obj has container) || (obj has supporter))
               objectloop (i in obj)
                       w = w + WeightOf (i);   ! Add weight of child-
                                               ! objects
       return w;
];

[ SizeOf obj;
       if (obj provides size)
       {
               if (metaclass(obj.size) == Routine)
                       return indirect (obj.size);
               return obj.size;
       };
       return 0;
];

[ VolumeOf obj;
       if (obj provides volume)
       {
               if (metaclass(obj.volume) == Routine)
                       return indirect (obj.volume);
               return obj.volume;
       };
       return 0;
];


! --------------------------------------------------------

! The following functions calculate the capacity of any given
! container or supporter.

[ CapacityWeightOf obj;
       if (obj provides capacity_weight)
       {
               if (metaclass(obj.capacity_weight) == Routine)
                       return indirect (obj.capacity_weight);
               return obj.capacity_weight;
       }
       return INFINITE_CAPACITY;        ! Unlimited weight capacity
];

[ CapacityVolumeOf obj;
       if (obj provides capacity_volume)
       {
               if (metaclass(obj.capacity_volume) == Routine)
                       return indirect (obj.capacity_volume);
               return obj.capacity_volume;
       }
       return INFINITE_CAPACITY;        ! Unlimited volume capacity
];

[ CapacitySizeOf obj;
       if (obj provides capacity_size)
       {
               if (metaclass(obj.capacity_size) == Routine)
                       return indirect (obj.capacity_size);
               return obj.capacity_size;
       }
       return INFINITE_CAPACITY;        ! Unlimited size capacity
];

[ CapacityNumberOf obj;
       if (obj provides capacity_number)
       {
               if (metaclass(obj.capacity_number) == Routine)
                       return indirect (obj.capacity_number);
               return obj.capacity_number;
       }
       return INFINITE_CAPACITY;        ! Unlimited number of objects
];


! --------------------------------------------------------


Class Receptacle
 with
       before [ s i;

       Receive:
       ! Check weight:
               if ( CapacityWeightOf (self) ~= INFINITE_CAPACITY )
                                                      ! not unlimited
                                                      ! capacity
               {
                       if ( WeightOf(noun) > CapacityWeightOf (self) )
                               print_ret (The) noun, " is too heavy for ", (the) self, ".";
                       s = 0;
                       objectloop (i in self)         ! calculate the
                               s = s + WeightOf(i);   ! weight of all
                                                      ! contents
                       if ( (s + WeightOf(noun)) > CapacityWeightOf (self) )
                               print_ret (The) self, " has reached it's capacity.";
               };

       ! Check volume:
               if ( CapacityVolumeOf (self) ~= INFINITE_CAPACITY )
                                                      ! not unlimited
                                                      ! capacity
               {
                       if ( VolumeOf(noun) > CapacityVolumeOf (self) )
                               print_ret (The) noun, " does not fit into ", (the) self, ".";
                       s = 0;
                       objectloop (i in self)         ! calculate the
                               s = s + VolumeOf(i);   ! volume of all
                                                      ! contents
                       if ( (s + VolumeOf(noun)) > CapacityVolumeOf (self) )
                               print_ret (The) self, " has reached it's capacity.";
               };

       ! Check size:
               if ( CapacitySizeOf (self) ~= INFINITE_CAPACITY )
                                                      ! not unlimited
                                                      ! capacity
               {
                       if ( SizeOf(noun) > CapacitySizeOf (self) )
                               print_ret (The) noun, " is too large for ", (the) self, ".";
               };

       ! Check number:
               if ( CapacityNumberOf (self) ~= INFINITE_CAPACITY )
                                                      ! not unlimited
                                                      ! capacity
               {
                       s = 0;
                       objectloop (i in self)
                               s++;
                       if ( (s+1) > CapacityNumberOf (self) )
                               print_ret (The) self, " contains too many objects.";
               };

               rfalse;
       ];



#ifdef DEBUG;

Verb meta       '$weigh'          * noun                -> MetaWeigh;
Verb meta       '$measure'        * noun                -> MetaMeasure;
Verb meta       '$capacity'       * noun                -> MetaCapacity;

[ MetaWeighSub;
               print_ret (The) noun, " weighs ",
               WeightOf (noun), " units.";
];
[ MetaMeasureSub;
               print (The) noun, ":^";
               print "Weight: ", WeightOf(noun), " units^";
               print "Size: ",   SizeOf(noun),   " units^";
               print "Volume: ", VolumeOf(noun), " units^";
               rtrue;
];
[ MetaCapacitySub;
               print (The) noun, ":^";

               print "Capacity (weight): ";
               if (CapacityWeightOf(noun) == INFINITE_CAPACITY)
                       print "infinite^";
               else
                       print CapacityWeightOf(noun), " units^";

               print "Capacity (size): ";
               if (CapacitySizeOf(noun) == INFINITE_CAPACITY)
                       print "infinite^";
               else
                       print CapacitySizeOf(noun), " units^";

               print "Capacity (volume): ";
               if (CapacityVolumeOf(noun) == INFINITE_CAPACITY)
                       print "infinite^";
               else
                       print CapacityVolumeOf(noun), " units^";

               print "Capacity (number of objects): ";
               if (CapacityNumberOf(noun) == INFINITE_CAPACITY)
                       print "infinite^";
               else
                       print CapacityNumberOf(noun), "^";

               rtrue;
];

#endif;

! End