/* Copyright (c) 1999, 2000 by Kevin Forchione. All Rights Reserved. */
/*
* TADS ADV.T/STD.T LIBRARY EXTENSION
* SMARTLIST.T
* version 1.0
*
* smartlist.t provides an enhancement of the standard ADV.T listing
* functions. These enhancements include:
*
* smartlist
* Useful in debugging, smartlist displays objects of various
* datatypes.
* listcont
* listcontcont
* The use of lists or objects as function parameters
* showcontcont
* itemXcnt
* listXcont
* Display of Actors in nestedrooms
* listfixedcont
* Display of fixeditems in room listings when Actor is inside
* of enterable class objects.
*
*----------------------------------------------------------------------
* REQUIREMENTS
*
* + HTML TADS 2.2.6 or later
* + Requires ADV.T and STD.T
* + Should be #included after ADV.T and STD.T.
*
*----------------------------------------------------------------------
* IMPORTANT LIBRARY INTERFACE AND MODIFICATION
*
* This module modifies the functioning of the following ADV.T
* listing functions:
*
* + listcont()
* + listcontcont()
* + showcontcont()
*
*----------------------------------------------------------------------
* COPYRIGHT NOTICE
*
* You may modify and use this file in any way you want, provided that
* if you redistribute modified copies of this file in source form, the
* copies must include the original copyright notice (including this
* paragraph), and must be clearly marked as modified from the original
* version.
*
*------------------------------------------------------------------------------
* REVISION HISTORY
*
* 12-May-99: Creation.
*/
/*
* smartlist: function( list, origlen )
*
* This is a recursive function that is useful for displaying
* information in nested lists. It will separate each item by
* a space, and separate sublists with '[ ... ]' indicators.
*
* Method is passed a list and the length of the list, which is
* used to determine spacing.
*/
smartlist: function( list, origlen )
{
local o;
if ( list = nil or length( list ) = 0 ) return;
if ( origlen = 2 )
{
if ( length( list ) < origlen )
" and ";
}
else if ( origlen > 2 )
{
if ( length( list ) = 1 )
", and ";
else if ( length( list ) < origlen )
", ";
}
o := car( list );
switch( datatype( o ) )
{
case 1: // number
say( o );
break;
case 2: // object
if ( proptype( o, &sdesc ) <> 5 )
o.sdesc;
else
"(unnamed_object)";
break;
case 3: // string
say( o );
break;
case 5: // nil
"(nil)";
break;
case 7: // list
"[";
smartlist( o, length( o ) );
"]";
break;
case 8: // true;
"(true)";
break;
case 10: // function pointer
"(function_pointer)";
break;
case 13: // property pointer
"(property_pointer)";
break;
default:
}
list := cdr( list );
smartlist( list, origlen );
}
/*
* listfixedcont: function(obj)
*
* This function is similar to listcont(obj).
*
* This function displays the fixeditem / actor contents of an
* object/list, separated by commas. The thedesc properties of the
* contents are used. It is up to the caller to provide the
* introduction to the list (usually something to the effect of "The
* box contains" is displayed before calling listXfixedcont) and
* finishing the sentence (usually by displaying a period). An object
* is listed only if its isactor or isfixed property is true. If there
* are multiple indistinguishable items in the list, the items are
* listed only once (with the number of the items).
*/
listfixedcont: function(obj)
{
local i, count, tot, list, cur, disptot := 0, prefix_count;
if (datatype(obj) = 2)
list := obj.contents;
else
list := obj;
tot := length(list);
count := 0;
for (i := 1; i <= tot; ++i)
{
if ( list[i].isactor or list[i].isfixed )
disptot++;
}
for (i := 1 ; i <= tot ; ++i)
{
cur := list[i];
if (cur.isactor or cur.isfixed)
{
/* presume there is only one such object */
prefix_count := 1;
/*
* if this is one of more than one equivalent items, list it
* only if it's the first one, and show the number of such
* items along with the first one
*/
if (cur.isEquivalent)
{
local before, after;
local j;
local sc;
sc := firstsc(cur);
for (before := after := 0, j := 1 ; j <= tot ; ++j)
{
if (isIndistinguishable(cur, list[j]))
{
if (j < i)
{
/*
* note that objects precede this one, and
* then look no further, since we're just
* going to skip this item anyway
*/
++before;
break;
}
else
++after;
}
}
/*
* if there are multiple such objects, and this is the
* first such object, list it with the count prefixed;
* if there are multiple and this isn't the first one,
* skip it; otherwise, go on as normal
*/
if (before = 0)
prefix_count := after;
else
continue;
}
if (count > 0)
{
if (count+1 < disptot)
", ";
else if (count = 1)
" and ";
else
", and ";
}
/* list the object, along with the number of such items */
if (prefix_count = 1)
cur.adesc;
else
{
sayPrefixCount(prefix_count); " ";
cur.pluraldesc;
}
/* show any additional information about the item */
if (cur.isworn)
" (being worn)";
if (cur.islamp and cur.islit)
" (providing light)";
count := count + 1;
}
}
}
/*
* listcont: function(obj)
*
* This function performs exactly as ADV.T listcont(obj). The
* only difference is that it accepts a list or an object.
*
* This function displays the contents of an object/list, separated
* by commas. The thedesc properties of the contents are used.
* It is up to the caller to provide the introduction to the list
* (usually something to the effect of "The box contains" is
* displayed before calling listXcont) and finishing the
* sentence (usually by displaying a period). An object is listed
* only if its isListed property is true. If there are
* multiple indistinguishable items in the list, the items are
* listed only once (with the number of the items).
*/
replace listcont: function(obj)
{
local i, count, tot, list, cur, disptot, prefix_count;
if (datatype(obj) = 2 )
list := obj.contents;
else
list := obj;
tot := length(list);
count := 0;
disptot := itemcnt(list);
for (i := 1 ; i <= tot ; ++i)
{
cur := list[i];
if (cur.isListed)
{
/* presume there is only one such object */
prefix_count := 1;
/*
* if this is one of more than one equivalent items, list it
* only if it's the first one, and show the number of such
* items along with the first one
*/
if (cur.isEquivalent)
{
local before, after;
local j;
local sc;
sc := firstsc(cur);
for (before := after := 0, j := 1 ; j <= tot ; ++j)
{
if (isIndistinguishable(cur, list[j]))
{
if (j < i)
{
/*
* note that objects precede this one, and
* then look no further, since we're just
* going to skip this item anyway
*/
++before;
break;
}
else
++after;
}
}
/*
* if there are multiple such objects, and this is the
* first such object, list it with the count prefixed;
* if there are multiple and this isn't the first one,
* skip it; otherwise, go on as normal
*/
if (before = 0)
prefix_count := after;
else
continue;
}
if (count > 0)
{
if (count+1 < disptot)
", ";
else if (count = 1)
" and ";
else
", and ";
}
/* list the object, along with the number of such items */
if (prefix_count = 1)
cur.adesc;
else
{
sayPrefixCount(prefix_count); " ";
cur.pluraldesc;
}
/* show any additional information about the item */
if (cur.isworn)
" (being worn)";
if (cur.islamp and cur.islit)
" (providing light)";
count := count + 1;
}
}
}
/*
* listcontcont: function(obj)
*
* This function performs exactly as ADV.T listcontcont(obj). The
* only difference is that it accepts a list or an object.
*
* This function lists the contents of the contents of an
* object/list. It displays full sentences, so no introductory or
* closing text is required. Any item in the contents list of the
* object obj whose contentsVisible property is true has
* its contents listed. An Object whose isqcontainer or
* isqsurface property is true will not have its
* contents listed.
*/
replace listcontcont: function(obj)
{
local list, i, tot;
if (datatype(obj) = 2)
list := obj.contents;
else
list := obj;
tot := length(list);
i := 1;
while (i <= tot)
{
showcontcont(list[i]);
i := i + 1;
}
}
/*------------------------------------------------------------------------------
* FUNCTIONS USED TO DISPLAY ACTORS IN NESTED ROOMS
*----------------------------------------------------------------------------*/
/*
* This function has been modified to use the new itemXcnt() and
* listXcont() functions, which take into account items and Actors.
*/
replace showcontcont: function(obj)
{
if (itemXcnt(obj.contents))
{
if (obj.issurface)
{
if (not obj.isqsurface)
{
"Sitting on "; obj.thedesc;" is "; listXcont(obj);
". ";
}
}
else if (obj.contentsVisible and not obj.isqcontainer)
{
caps();
obj.thedesc; " seem";
if (!obj.isThem) "s";
" to contain ";
listXcont(obj);
". ";
}
}
if (obj.contentsVisible and not obj.isqcontainer)
listfixedcontcont(obj);
}
/*
* itemXcnt: function( list )
*
* This functions exactly as itemcnt() does, except that it includes
* Actors into its totals. Any object that has .isactor = true will
* be included.
*/
itemXcnt: function(list)
{
local cnt, tot, i, obj, j;
tot := length(list);
for (i := 1, cnt := 0 ; i <= tot ; ++i)
{
/* only consider this item if it's to be listed */
obj := list[i];
if (obj.isListed or obj.isactor)
{
/*
* see if there are other equivalent items later in the
* list - if so, don't count it (this ensures that each such
* item is counted only once, since only the last such item
* in the list will be counted)
*/
if (obj.isEquivalent)
{
local sc;
sc := firstsc(obj);
for (j := i + 1 ; j <= tot ; ++j)
{
if (isIndistinguishable(obj, list[j]))
goto skip_this_item;
}
}
/* count this item */
++cnt;
skip_this_item: ;
}
}
return cnt;
}
/*
* listXcont: function( obj )
*
* This functions exactly as listcont(), except that it takes Actors into
* account. We don't replace listcont(), however, because it's only when
* actors are inside nested rooms that we want to list them in this manner.
* When they are inside the player's location
* we use the normal method.
*/
listXcont: function(obj)
{
local i, count, tot, list, cur, disptot, prefix_count;
if (datatype(obj) = 2)
list := obj.contents;
else
list := obj;
tot := length(list);
count := 0;
disptot := itemXcnt(list);
for (i := 1 ; i <= tot ; ++i)
{
cur := list[i];
if (cur.isListed or cur.isactor)
{
/* presume there is only one such object */
prefix_count := 1;
/*
* if this is one of more than one equivalent items, list it
* only if it's the first one, and show the number of such
* items along with the first one
*/
if (cur.isEquivalent)
{
local before, after;
local j;
local sc;
sc := firstsc(cur);
for (before := after := 0, j := 1 ; j <= tot ; ++j)
{
if (isIndistinguishable(cur, list[j]))
{
if (j < i)
{
/*
* note that objects precede this one, and
* then look no further, since we're just
* going to skip this item anyway
*/
++before;
break;
}
else
++after;
}
}
/*
* if there are multiple such objects, and this is the
* first such object, list it with the count prefixed;
* if there are multiple and this isn't the first one,
* skip it; otherwise, go on as normal
*/
if (before = 0)
prefix_count := after;
else
continue;
}
if (count > 0)
{
if (count+1 < disptot)
", ";
else if (count = 1)
" and ";
else
", and ";
}
/* list the object, along with the number of such items */
if (prefix_count = 1)
cur.adesc;
else
{
sayPrefixCount(prefix_count); " ";
cur.pluraldesc;
}
/* show any additional information about the item */
if (cur.isworn)
" (being worn)";
if (cur.islamp and cur.islit)
" (providing light)";
count := count + 1;
}
}
}