/* 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. */ #define __SMARTLIST_MODULE_ smartlist: function; listfixedcont: function; itemXcnt: function; listXcont: function; /* * 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; } } }