!------------------------------------------------------------------------
! Objlstr.h   Version 1.50                                A Debugging Aid
! Marnie Parker    aka FemaleDeer aka Doe    [email protected]    3/18/00
!------------------------------------------------------------------------
! One could properly call this include file essentially a hack. The code I
! wrote myself is very simple. The main code comes from Tony Lewis's
! debugrtn.h (a forever-to-be-unreleased h file -- I swear there is no
! connection between Tony sharing his code with me and his later strange
! disappearance ;-)) and Graham Nelson's parserm.h.
!
! Back when I was writing Paradox programs (which uses forms that have
! fields that can be defined with "pictures") I often used a third party
! product that would print out a list of those fields and their pictures.
! Very handy, because double checking them while in the program was tedious.
!
! So I wished for something similar for Inform. An object lister that
! would help me debug my games. (Because Showobj only shows one at a time.)
! Instead of poring over code to double check whether I had made something I
! wanted to be static, static (it's lots a fun when the player can walk off
! with a door), I wanted a list that would show me right away. Also one to
! quickly show me if my room directions matched up (player exits the tunnel
! south and enters the cave, player exits the cave north and enters the tunnel).
!
! This file is the result of that wishing. Not a perfect solution, because
! the information in routines will not be printed.
!
! Here's to better debugging (yeah!). Sorry this can't also help you with
! buggy routines (ah, there's the rub). :-) Doe
!
! Version 1.1 makes including certain objects optional so the user doesn't
! have to comment them out, but comment them in when they want to include
! them. A helpful change suggested by Roger Firth.
!
! Notes in this file break down into usage (how to use this as a debugging
! aid) and programming (what specific programming "tricks" are used).
!
! Usage:
!
! Include after parser.h and also invoke DEBUG (Constant Debug;) as this
! file uses Showobj. Script on before you list something so you can save
! it to a file for later printing. Script off, of course, when done.
!
! A list of objects, each object separated by a line, meeting the conditions
! you select will print to screen (and also be saved to file, with script on).
!
! To exclude some objects from inclusion see Special Objects below.
!
! Commands:
! list rooms                    -- will list all the rooms
! list dirs                     -- will list all rooms showing JUST directions
!                                  (other information included in list rooms is
!                                  omitted so one can debug directions quickly)
! list takeable                 -- will list all takeable objects
! list hidden                   -- will list all hidden ("takeable") objects
! list nontakeable              -- will list all nontakeable objects
! list has <attr1> <attr2> <attr3>
!                               -- will list all objects with these attributes
!                                  (limit of three attributes)
! Examples:
! list has supporter            -- will list all supporters
! list has supporter static     -- will list all static supporters
! list has animate              -- will list all creatures
! list has animate neuter       -- will list all neuter creatures
! list has door lockable locked -- will list all locked un/lockable doors
!
! list with <prop1> <prop2> <prop3>
!                               -- will list all objects with these properties
!                                  (limit of three properties, although
!                                  combinations are less useful here)
! Examples:
! list with found_in            -- will list all floating objects
! list with time_out            -- will list all objects that use timers
! list with daemon              -- will list all objects with daemons
!
! list has <attr1> <attr2> <attr3> with <prop1> <prop2> <prop3>
!                               -- will list all objects with these attributes
!                                  AND properties (limit of three each)
! Example:
! list has door with with_key   -- will list all doors with keys
!
! list with <prop1> <prop2> <prop3> has <attr1> <attr2> <attr3>
!                               -- same as the above, just reversed wording
!
! Note: Entering a partial string may include a similar property or attribute.
! Example, "list with door", will list all objects with door_to and door_dir.
! But if you enter an invalid attribute or property, this file will print
! an error message <invalid attribute or property>. Then Inform will add,
! "I didn't understand that sentence.". The additional line has to do
! with the way Inform processes verbs and can't be skipped.


!-----------------------------------------------------------------------
! Others' Routines
!-----------------------------------------------------------------------
#ifdef DEBUG;

Array check_attr ->64;
Array attr_list -->48;
Array check_prop ->64;
Array prop_list -->63;

! Programming Note - The next four routines are lifted from Tony Lewis'
! debugrtn.h. I would never had known how to write z-machine memory to
! variables without having seen his code.

[ AttrCompare a word l anames i;

   anames = #identifiers_table;
   anames = anames + 2 * (anames-->0);

   check_attr-->0 = 0;
   @output_stream 3 check_attr;
   print (string) anames-->a;
   @output_stream -3;

   for (i = 0 : i < l: i++){
       if (check_attr->(2+i) >= 'A' && check_attr->(2+i) <= 'Z')
           check_attr->(2+i) = check_attr->(2+i) + ('a' - 'A');
       if (check_attr->(2+i) ~= word->i) rfalse;
   }
   rtrue;
];

[ MakeAttrList n w;

 w = NextWordStopped();
 if (w == -1) return -1;

 attr_list-->0 = 0;

 do
 {  for (n=0: n < 48: n++)
        if (AttrCompare(n, WordAddress(wn-1), WordLength(wn-1))) break;
    if (n >= 0 && n < 48)
    {
      (attr_list-->0)++;
      attr_list-->(attr_list-->0) = n;
    }
    else
    {
       print "^<invalid attribute, ~";
       PrintByteArray(WordAddress(wn-1), WordLength(wn-1));
       print "~>^";
       return -1;
    }
    w = NextWordStopped();

 } until (w == -1 or 'with');

if (attr_list-->0 == 0) return -1;
if (w=='with'){
   n = MakePropList();
   return n;
}
return 0;
];

! Programming Note - Next two routines are altered quite a bit from debugrtn.h.

[ PropertyCompare p word l i;

   check_prop-->0 = 0;
   @output_stream 3 check_prop;
   print (property) p;
   @output_stream -3;

   for (i = 0 : i < l: i++ )
   {
       if (check_prop->(2+i) >= 'A' && check_prop->(2+i) <= 'Z')
           check_prop->(2+i) = check_prop->(2+i) + ('a' - 'A');
       if (check_prop->(2+i) ~= word->i) rfalse;
   }
   rtrue;
];

[ MakePropList n l w;

 w = NextWordStopped();
 if (w == -1) return -1;

 prop_list-->0 = 0;
 l = #identifiers_table-->0;

 do
 {  for (n=1: n < l: n++){
        if (n~=2 or 3)
           if (PropertyCompare(n, WordAddress(wn-1), WordLength(wn-1))) break;
    }
    if (n >= 0 && n < l)
    {
      (prop_list-->0)++;
       prop_list-->(prop_list-->0) = n;
    }
    else
    {
       print "^<invalid property, ~";
       PrintByteArray(WordAddress(wn-1), WordLength(wn-1));
       print "~>^";
       return -1;
    }
    w = NextWordStopped();

 } until (w == -1 or 'has');

if (prop_list-->0 == 0) return -1;
if (w=='has'){
   n = MakeAttrList();
   return n;
}
return 0;
];

! Programming Note - Lifted directly from Graham Nelson's paserm.h
! (originally Showobj) and truncated to show JUST directions.

[ ShowDirsSub c f l a n x;
  if (noun==0) noun=location;
  objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; }
  if (f == 1) print (name) l, " ~"; else print "Object ~";
  print (name) noun, "~ (", noun, ")";
  if (parent(noun)~=0) print " in ~", (name) parent(noun), "~";
  new_line;
  if (f > 1)
  {   print "  class ";
      objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " ";
      new_line;
  }
  for (a=0,f=0:a<48:a++) if (noun has a) f=1;
  if (f)
  {   print "  has ";
      for (a=0:a<48:a++) if (noun has a) print (DebugAttribute) a, " ";
      new_line;
  }
  if (noun ofclass Class) return;

! Altered slightly
  f=0; l = #identifiers_table-->0;
  for (a=1:a<=l:a++)
  {  if ((a~=2 or 3) && noun.&a)
     {  if (a ==  u_to or d_to or n_to or s_to or e_to or w_to or
            ne_to or nw_to or se_to or sw_to or in_to or out_to or cant_go)
           {  if (f==0) { print "  with "; f=1; }
              print (property) a;
              n = noun.#a;
              for (c=0:2*c<n:c++)
              {   print " ";
                  x = (noun.&a)-->c;
                  switch(x)
                  {   NULL: print "NULL";
                      0: print "0";
                      1: print "1";
                      default:
                      switch(metaclass(x))
                      {   Class, Object: print (name) x;
                          String: print "~", (string) x, "~";
                          Routine: print "[...]";
                      }
                      print " (", x, ")";
                  }
             }
             if (f==1) print ",^       ";
          }
     }
  }
  new_line;
];

!----------------------------------------------------------------------
! List Routines - The simple little routines I wrote.
!----------------------------------------------------------------------

! Usage Note - If this routine this causes formating problems on your
! screen just replace where it appears with print
! "^----------------------^"; or something.

[ PrintLine col i;
   col = 0->33;
   if (col==0) col=80;
   for (i = 0 : i < col : i++)
       print (char) '-';
   print "^";
];

! Programming Note - Prints byte "string" for error messages.

[ PrintByteArray arry l i;
 for (i=0: i < l: i++)
     print (char) arry->i;
];

! Programming Note - These listers use a simple loop to go through the game
! and show ALL the objects that match a set of conditions. This basic routine
! can be adapted for any combination of conditions. Which I have done, below.

! Usage Note - Special Objects
!
! Remove the exclamation point indicating a comment to exclude these objects
! when you are including these:
!
! topics - info.h by Jesse Burneko or another conversation system
!          that uses topics
!
! temp_obj - L. Ross Raszewski's utility.h
!
! LibraryMessages - when you include your own LibraryMessages in your game
!
! Note:  an unamed object will show up in the hidden list when using altmenu.h.
!
! By excluding these objects from lists, you won't have internal Inform
! objects listed, which can confuse matters. For instance, temp_obj
! showing up in a list of hidden objects.

! Programming Note - ofclass Class refers to an original class declaration and
! not to a specific object of a particular class.

[ ListRoomsSub o i;

 for (o = player: o <= top_object: o++)
 {   if ((~~(o ofclass Class)) &&
         (parent(o)==0) && ((o provides u_to) || (o provides d_to) ||
         (o provides n_to) || (o provides s_to) ||  (o provides e_to) ||
         (o provides w_to) || (o provides ne_to) || (o provides nw_to) ||
         (o provides se_to) || (o provides sw_to) || (o provides cant_go))){
         i++;
         <Showobj o>;
         PrintLine();
     }
 }
 if (i==0) print "^There are no rooms.^";
];

[ ListDirsSub o i;
 for (o = player: o <= top_object: o++)
 {   if ((~~(o ofclass Class)) &&
         (parent(o)==0) && ((o provides u_to) || (o provides d_to) ||
         (o provides n_to) || (o provides s_to) || (o provides e_to) ||
         (o provides w_to) || (o provides ne_to) || (o provides nw_to) ||
         (o provides se_to) || (o provides sw_to) || (o provides cant_go))){
         i++;
         <ShowDirs o>;
         PrintLine();
     }
 }
 if (i==0) print "^There are no rooms.^";
];

[ ListTakeableSub o i;
 for (o = player: o <= top_object: o++)
 {   if ((~~(o ofclass Class)) &&
        (parent(o)~=0) && (o hasnt animate) && (o hasnt scenery) &&
        (o hasnt static) && (o hasnt concealed)

! info.h    -- && (o notin topics)

        ){
        i++;
        <Showobj o>;
        PrintLine();
    }
 }
 if (i==0) print "^There are no takeable objects.^";
];

! Usage Note - There could be objects that have no parent and aren't rooms,
! (although rooms which have no directions will show up here too), as they
! haven't been been found by the player yet. (i.e, They are deliberately left
! unconnected to the object tree by the game writer to "hide" them.)
! Not all of the hidden objects listed will also be takeable, just most.

[ ListHiddenSub o i;
 for (o = player: o <= top_object: o++)
 {   if ((~~(o ofclass Class)) &&
        (parent(o)==0) && (~~((o provides u_to) || (o provides d_to) ||
        (o provides n_to) || (o provides s_to) || (o provides e_to) ||
        (o provides w_to) || (o provides ne_to) || (o provides nw_to) ||
        (o provides se_to) || (o provides sw_to) || (o provides cant_go))) &&
        (o hasnt animate) && (o ~=InformParser or InformLibrary

! your own   -- or LibraryMessages
! info.h     -- or topics
! utility.h  -- or temp_obj

        or thedark or compass)){
        i++;
        <Showobj o>;
        PrintLine();
    }
 }
 if (i==0) print "^There are no hidden objects.^";
];

[ ListNonTakeableSub o i;
 for (o = player: o <= top_object: o++)
 {   if ((~~(o ofclass Class)) &&
        (parent(o)~=0) && ((o has scenery) || (o has static) ||
        (o has concealed)) && (o hasnt animate) && (o notin compass)

! info.h    --  && (o notin topics)

        ){
        i++;
        <Showobj o>;
        PrintLine();
    }
 }
 if (i==0) print "^There are no nontakeable objects.^";
];

[ ListAllSub o i;
 for (o = player: o <= top_object: o++){
     if ((~~(o ofclass Class)) &&
         (o~=InformParser or InformLibrary

! your own  -- or LibraryMessages
! info.h    -- or topics
! utility.h -- or temp_obj

         or thedark or compass) &&
         (o notin compass)

! info.h    -- && (o notin topics)

         ){
         i++;
         <Showobj o>;
         PrintLine();
     }
 }
 if (i==0) print "^There are no listable objects.^";
];

! Usage Note - Aliases cannot be listed specifically as attributes and their
! aliases cannot be split.
!
! Example: attribute out_doors alias proper
! >list has proper   -- will list both (since they cannot be listed separately)
! >list has out_door -- will not work  (since proper is the real attribute, the
!                                       condition if has out_door means Inform
!                                       really searches for if has proper)

[ ListByAttrSub a b c o max i;

  max = attr_list-->0;
  a = b = c = attr_list-->1;

  if (max > 1) {
     c = attr_list-->2;
     if (max > 2) b = attr_list-->3;
  }

  for (o = player: o <= top_object: o++){
      if (o has a && o has b && o has c)
      { i++; <Showobj o>; PrintLine(); }
  }

  if (i==0) print "^There are no objects with that/those attribute/s.^";
];

! Usage Note - Ditto for aliases for properties (see ListByAttr comments).
! But this WILL list local properties, they don't have to be global.

[ ListByPropSub a b c o max i;

  max = prop_list-->0;
  a = b = c = prop_list-->1;

  if (max > 1) {
     c = prop_list-->2;
     if (max > 2) b = prop_list-->3;
  }

  for (o = player: o <= top_object: o++){
      if (o provides a && o provides b && o provides c)
      { i++; <Showobj o>; PrintLine(); }
  }

  if (i==0) print "^There are no objects with that/those property/ies.^";
];

[ ListByBothSub a b c d e f o maxa maxp i;

  maxa = attr_list-->0;
  maxp = prop_list-->0;

  a = b = c = attr_list-->1;
  if (maxa > 1) {
     c = attr_list-->2;
     if (maxa > 2) b = attr_list-->3;
  }

  d = e = f = prop_list-->1;
  if (maxp > 1) {
     f = prop_list-->2;
     if (maxp > 2) e = prop_list-->3;
  }

  for (o = player: o <= top_object: o++){
      if (o has a && o has b && o has c && o provides d && o provides e && o provides f)
      { i++; <Showobj o>; PrintLine(); }

  }

  if (i==0) print "^There are no objects with that/those property/ies.^";
];

!----------------------------------------------------------------------
! Verb Declarations
!----------------------------------------------------------------------

! Programming Note - 'has' MakeAttrList 'with' (blank) -- is a way to fool
! Inform. Only one routine is allowed on a verb parameter line, so
! MakeAttrList() itself will call MakePropList() if the word 'with' is
! entered. Same for the reverse.

Verb meta 'list'
               * 'rooms'                                -> ListRooms
               * 'dirs'/'directions'                    -> ListDirs
               * 'takeable'                             -> ListTakeable
               * 'hidden'                               -> ListHidden
               * 'nontakeable'/'non-takeable'/'notake'  -> ListNonTakeable
               * 'all'                                  -> ListAll
               * 'has' MakeAttrList                     -> ListByAttr
               * 'with' MakePropList                    -> ListByProp
               * 'has' MakeAttrList 'with'              -> ListByBoth
               * 'with' MakePropList 'has'              -> ListByBoth;

#endif;