!This is an example program which uses Kory Heath's revised
!'ReviseMulti' function which gives control back to the programmer, so
!that you can allow commands like 'TAKE ALL' to work for items in
!containers or on supporters--something Inform disallows in its current
!state (6.15).  To use it in your own code, be sure to put the 'Replace
!ReviseMulti' line in *before* you include the parser, and then include
!the new Revise Multi code in wherever you want.  The ChooseObjects
!function in this code is just an example--you can vary this widely,
!depending on what effect you want.


Constant Story "The Chooser";
Constant Headline "^An Interactive Worked Example^
Compiled by Lucian Smith, but largely written by Kory Heath^";
Release 1;

Replace ReviseMulti;

Include "Parser";
Include "VerbLib";

[ Initialise;
 location = Room3;
 give player light;
"^^^^^Life is pretty routine.  Use GET ALL to pick up everything on
the floor, and GET ALL FROM CONTAINER if you want things from inside
other items.  Then one day, you notice a staggeringly useful library
hack by Kory Heath.  Has it been here before?^";
];

!----------------------------------------------------------------------------
!  ReviseMulti  revises the multiple object which already exists, in the
!    light of information which has come along since then (i.e., the second
!    parameter).  It returns a parser error number, or else 0 if all is well.
!    This only ever throws things out, never adds new ones.
!
!  This is my modified version of this library function.  It calls
!    ChooseObjects() before it throws out any objects, which overrides
!    or accepts in the normal fashion.
!
!  Original function found in parserm.h   line: 2374
!
!  -Kory Heath
!   [email protected]
!----------------------------------------------------------------------------

[ ReviseMulti second_p  i low;

#ifdef DEBUG;
 if (parser_trace>=4)
     print "   Revising multiple object list of size ", multiple_object-->0,
           " with 2nd ", (name) second_p, "^";
#endif;

 if (multi_context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
 {   for (i=1, low=0:i<=multiple_object-->0:i++)
     {   if ( (multi_context==MULTIEXCEPT_TOKEN
               && multiple_object-->i ~= second_p)
              || (multi_context==MULTIINSIDE_TOKEN
                  && multiple_object-->i in second_p))
         {
           if (ChooseObjects(multiple_object-->i,4,second_p)~=2)
           {
             low++;
             multiple_object-->low = multiple_object-->i;
           }
         }
         else
         {
           if (ChooseObjects(multiple_object-->i,3,second_p)==1)
           {
             low++;
             multiple_object-->low = multiple_object-->i;
           }
         }
     }
     multiple_object-->0 = low;
 }

 if (multi_context==MULTI_TOKEN && action_to_be == ##Take)
 {   for (i=1, low=0:i<=multiple_object-->0:i++)
         if (parent(multiple_object-->i)==parent(actor)) low++;
#ifdef DEBUG;
     if (parser_trace>=4)
         print "   Token 2 plural case: number with actor ", low, "^";
#endif;
     if (take_all_rule==2 || low>0)
     {   for (i=1, low=0:i<=multiple_object-->0:i++)
         {   if (parent(multiple_object-->i)==parent(actor))
             {
               if (ChooseObjects(multiple_object-->i,4,second_p)~=2)
               {
                 low++;
                 multiple_object-->low = multiple_object-->i;
               }
             }
             else
             {
               if (ChooseObjects(multiple_object-->i,3,second_p)==1)
               {
                 low++;
                 multiple_object-->low = multiple_object-->i;
               }
             }
         }
         multiple_object-->0 = low;
     }
 }

 i=multiple_object-->0;
#ifdef DEBUG;
 if (parser_trace>=4)
     print "   Done: new size ", i, "^";
#endif;
 if (i==0) return NOTHING_PE;
 return 0;
];


!Now, to use it:  normally ChooseObjects is called with a 0 or 1 passed
!into the "code" variable, signifying whether the object is about to be
!kept or thrown out of the multi-list on the first pass.  This behaviour
!remains the same, but now ChooseObjects will also be called on the
!*second* pass, with a 3 or 4 in the "code" variable.  You can keep or
!reject the object in the normal fashion.

!I've also added something else extra (which you can use or not): on the
!second pass, ChooseObjects is called with a third variable passed,
!indicating the second noun (if any).  So when the input is "GET ALL FROM
!BUCKET", ChooseObjects will be called (on the second-pass) with the
!Bucket object in the third variable.  Well, try it out and you'll see
!what I mean.

!Here's a sample ChooseObjects function that allows GET ALL to work with
!containters:

Constant FORCE_INCLUSION 1;
Constant FORCE_EXCLUSION 2;


[  ChooseObjects obj code second_p;
   switch (code)
   {
    2:
       rfalse;
    0,1:
       if (obj has scenery)
           return FORCE_EXCLUSION;
       rfalse;
    3,4:
       switch (action_to_be)
       {
        ##Take:
           if (parent(obj) has scenery || parent(obj) has static)
               return FORCE_INCLUSION;
           rfalse;
       }
       rfalse;
   }
   rfalse;
];

Object Room3 "Test Control Room #3"
  with  description "Everything's the same here, except for the way
          TAKE ALL works.";

Object -> acontainer "container"
  with description "It can contain things.",
       name 'container',
  has container open static;

Object -> -> stuff "stuff"
  with name 'stuff',
  has pluralname;

Object -> -> things "things"
  with name 'things',
  has pluralname;

Object -> -> thing "thing"
  with name 'thing';

Object -> -> somethingelse "something else"
  with name 'something' 'else';

Include "Grammar";