Lists and Listers

Works of Interactive Fiction often need to display lists. Typically these may be lists of objects visible in a room or a container, or a list of items held in the player character's inventory. There are generally two stages involved in displaying a list:

  1. Generating the list of items (usually objects) that need to be displayed.
  2. Formatting the list and displaying it on the screen. This stage may also include further filtering of the list.

The first stage is generally carried out by a method such as listContents() or listSubcontentsOf(), defined on Thing, or else a method define on the appropriate Action object, such as the execAction() method of the Inventory action. More will be said about such methods below. The second stage is carried out by a Lister, and it is Listers that we shall look at next.


Listers

The purpose of a Lister is to format and display a list. The various types of lister all descend from the Lister class, which defines the following methods:

From this you can able to see that the show() and buildList() methods are for use with an existing Lister (to make it display a list, or else return the list it would display in a single-quoted string), while all the rest are used when defining a Lister.

ItemLister

Many of the Listers actually used in a game are based on a subclass of Lister called ItemLister. This is base class of all the Listers used to list physical objects in a game. Several of the features of this Lister are defined in the language-specific part of the library, but the principal way in which an ItemLister customizes the base Lister class are as follows:

The language-specific part of the library in english.t defines the following additional methods on ItemLister:

Types of Lister

The adv3Lite defines a number of different types of Lister for various specific purposes. The definition of these listers is split between lister.t (which defines the base functionality) and english.t (which defines the language-specific aspects). The lister modifications in english.t generally do not use the BMsg()/DMsg() mechanism to generate text, since such indirection would be superfluous in a part of the library which is in any case language-specific and where modification may be more simply achieved by overriding the relevant methods such as showList(), showListPrefix(), showListSuffix() and showListEmpty().

The specific kinds of Lister defined in the library are:

Note that all but the last of the above descend from ItemLister.

Customizing Listers

Customizing a lister is basically a matter of overriding one or more of the methods and properties described above. Most typically you might want to change the way a list is introduced or concluded by overriding showListPrefix() or showListSuffix(). You might want to do this globally, in which case you can just modify the lister concerned, for example:

 modify lookLister
    showListPrefix(lst, pl, parent)
    {
        "Lying on the floor <<if pl>>are<<else>>is<<end>> ";
    }
    
    showListSuffix(lst, pl, parent)
    {
        ". ";
    }    
; 
 

This would then affect the way in which miscellaneous items were listed in every room in the game. More typically, though, we might want to affect the way things are listed in a particular room or on a particular object, in which case we can override the appropriate property of Thing to point to a custom Lister object:

To make use of these properties, you'd typically define your own custom lister based on the appropriate one from the library, and then attach it to the relevant property, probably as an anonymous object (unless you wanted to use the same custom lister on a number of different objects). For example, to customize the lister used when opening or looking in a box you could do something like this:

 + largeBox: OpenableContainer 'large box'
    
    myOpeningContentsLister: openingContentsLister
    {
        showListPrefix(lst, pl, parent)
        {
            "On ripping open the box {i} discover{s/ed} ";
        }
    }
    
    myLookInLister: lookInLister
    {
        showListPrefix(lst, pl, parent)
        {
            "Lurking inside the box <<if pl>>are<<else>>is<<end>> ";
        }
    }
;
 

For more information on customizing the way items are listed in room descriptions, see the chapters on Room Descriptions and (for remote rooms) SenseRegions.


Generating Lists

So far we have been looking at techniques for formatting lists once we have a list of objects to format. The other part of the process is generating the list of objects in the first place. This happens in various places in the library. It will not be appropriate to go into all of them in too much detail here. Instead we shall simply give an overview and refer the interested reader to the relevant parts of the Library Reference Manual for the nitty-gritty low-level detail.

The top-level method used to generate lists of objects for a room description is the Thing method listContents(). This has to generate not one but three lists: the list of items with specialDescs to show before the miscellaneous items, the list of miscellaneous items, and the list of items with specialDescs to show after the list of miscellaneous items. For items with specialDescs the method then just runs through each of the two lists showing the appropriate specialDesc or initSpecialDesc, having sorted the list in order of the specialDescOrder property. The list of miscellaneous items is displayed in between the two lists of items with specialDescs using the lister passed as a parameter to listContents(lister), which defaults to roomContentsLister (itself a property of Thing, which in turn defaults to lookLister, the actual Lister object employed, as noted above).

The listContents() method is further complicated by a number of other tasks it needs to perform, such as listing the contents of the player character's immediate location first, if the player character is in a nested room, ensuring that hidden items are excluded from the list, listing the contents of any remote locations if the senseRegion.t module is present, noting that any items listed have been seen by the player character, and listing any visible contents of any of the items just listed that want their contents listed (for which it calls the listSubContentsOf() method). From this it can be seen that listContents() has a complex and specialized task to perform, and is used only to generate the lists of items to be shown in a room description.

The Thing method listSubcontentsOf(contList, lister) is, as just mentioned, used to list the subcontents of the top-level contents of a room, but is used elsewhere in the library as well and could conceivably be called directly from game code. The contList parameter is supplied as a list of items (or a singleton item) whose contents are in turn to be listed. The optional lister parameter is the Lister object to be used to list the subcontents; if this parameter is not explicitly supplied it defaults to examineLister (which is in turn a property of Thing which defaults to the descContentsLister Lister objected, as noted above). The listSubContentsOf() method sorts the list passed to it in listOrder order and then excludes certain items from the list (those that are hidden, carried by an actor, impossible to see inside, or empty). It then goes through each item than remains in the list and divides it contents into objects with specialDescs to be listed before miscellaneous contents, the miscellaneous contents, and objects with specialDescs to be listed after the miscellaneous contents, excluding all objects that are hidden or already mentioned. Those items with specialDescs thus have their specialDescs shown, while any miscellaneous items are listed using the lister that was passed as the second parameter to listSubcontentsOf(). Finally, the contents of all these items are then listed with a recursive call to listSubContentsOf(). This may sound quite complicated, but the effect is to produce a complete list of everything that should be listed arranged in the correct order with specialDescs uses as appropriate and the listing carried on to the depth of nesting needed to list every visible object within the containment hierarchy of the list originally passed to listSubContentsOf(). In short, listSubcontentsOf() is the method to use to list the complete contents of anything (or a list of anything) that isn't a room.

In addition to being called from listContents(), listSubcontentsOf() is the method used to list the relevant contents of objects in response to an EXAMINE command (via the examineStatus() method), an OPEN command (when opening a container reveals its contents) and a LOOK IN, LOOK UNDER or LOOK BEHIND command. In the case of these last four commands, listSubcontentsOf() is called from the action() section of the relevant dobjFor() block.

Analogous methods are used to generate lists of items visible in remote locations when showing a room description. In particular the Thing method listRemoteContents(lst, lister, pov) overridden in senseRegion.t is used to list a set of items in lst from the point of view of an actor pov using the Lister lister. In essence it does much the same job for a remote location as listContents() does for the player character's immediate location, but is only relevant when two or more locations are connected by sight within a SenseRegion.

Lists are also generated and/or used at various other places in the library, such as in the definition of the Inventory action, the processOptions(lst) function used to display a list of options at the end of the game, a couple of places in actor.t that show lists of suggested topics, the examineStatus() method of the SimpleAttachable class and its subclasses (to list the attached objects), and the showFullScore() method of the libScore object (used to show a list of achievements). Interested readers are referred to the Library Reference Manual for details.


List-Related Functions

The language-specific part of the library (in english.t) defines a number of list-related functions that are used by the library and are also available to user code: