#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
/*
* listControl
*. version 0.2 (Beta) : 12-Jul-09
*. by Eric Eve
*/
//------------------------------------------------------------------------------
/*
* listControl provides three new functions:
*. stop(lst)
*. cycle(lst)
*. shuffle(lst)
*.
* The lst parameter can be supplied either as a single list (in square
* brackets) or as a number of parameters separated by commas: in other
* words the following two function calls are equivalent:
*.
*. stop(['screams', 'yells', 'cries again'])
*. stop('screams', 'yells', 'cries again')
*.
* The purpose of these functions is typically to provide minor variations
* of wording in larger pieces of text, for example:
*.
*. "<q>Absolutely not!</q> Bob <<stop('declares', 'insists', 'declares yet
*. again')>>. ";
*.
* These functions are thus a little similar to the rand() function already
* built in to TADS 3, and have been designed to use analogous syntax.
*.
*. stop() works like a StopEventList
*. cycle() works like a CyclicEventList
*. shuffle() works like a ShuffledEventList
*.
* IMPORTANT NOTE. In order for these three functions to work, the
* extension has to store data in relation to each list (each three
* function needs to keep track of where in the list it has got to). For
* this purpose, the extension needs some way of identifying which list is
* which, and it does this by looking at the elements of the list. It
* follows that if there are two lists with the same elements in your
* game, they'll be treated as if they were the same list. This may not
* matter for shuffle() but will matter a lot for stop().
*
* If this is likely to be an issue for your game, you can make each list
* that might otherwise be duplicated unique by adding a first element
* that's of a different data type from all the rest. For example, you
* could use a number or an object as the first element of a list of
* strings to make it a unique list of strings - the stop(), cycle(), and
* shuffle() functions will know to ignore this first item in their
* output. Thus, one way to ensure a stop() function treats its list as
* unique when it's used in a TopicEntry, say, might be to add 'self' as
* the first element of the list:
*
*. "<q>Absolutely not!</q> Bob <<stop(self, 'declares', 'insists',
*. 'declares yet again')>>. ";
*.
* This will work because 'self' will refer to a different object for every
* TopicEntry, thus guaranteeing to make the list unique (unless you use
* the same list more than once in the same TopicEntry, in which case you
* could distinguish them by making the first element [self, 1] then
* [self, 2] and so on).
*/
ModuleID
name = 'list control'
byline = 'by Eric Eve'
htmlByline = 'by <a href="mailto:
[email protected]">
Eric Eve</a>'
version = '0.2'
listingOrder = 70
;
//==============================================================================
/*
* The listControl object stores three lookup tables for use with the
* stop(), cycle() and shuffle() functions, together with some common code
* they each use.
*
* This works by storing a reference to the list for which we want the next
* item (in stopping, cyclic or shuffled order) in the appropriate
* LookupTable together with the current index value. The next index value
* is then calculated according to the kind of list, stored in the table
* and returned to the caller. We thus use the LookupTables to associate a
* numerical index value with each list.
*/
listControl: object
/*
* The LookupTables for use with the stop(), cycle() and shuffle()
* functions respectively
*/
stopTab = nil
cycleTab = nil
shuffleTab = nil
/*
* If the first element of the list is not of the same type as the
* second, then we assume that the first element is simply a marker to
* make the list unique, in which case we don't want to include it
* among the items to be returned from the list, so we start with the
* second element.
*/
getFirstIdx(lst)
{
if(lst.length > 1 && dataType(lst[1]) != dataType(lst[2]))
return 2;
return 1;
}
/*
* Find the appropriate LookupTable for the kind of list we want to
* reference, and create one if one does not already exist.
*/
getTable(prop)
{
local tab = self.(prop);
if(tab == nil)
{
self.(prop) = new LookupTable(10,10);
tab = self.(prop);
}
return tab;
}
/* Return the index of the next item we want from the current list */
getNextIdx(lst, tabProp, nextProp)
{
local tab = getTable(&tabProp);
local idx = tab[lst];
if(idx == nil)
idx = getFirstIdx(lst);
else
{
idx++;
if(idx > lst.length())
idx = self.(nextProp)(lst);
}
tab[lst] = idx;
return idx;
}
getNextStopIdx(lst) { return lst.length(); }
getNextCycleIdx(lst) { return getFirstIdx(lst); }
;
/*
* Each of these three functions provides a means of making the stored list
* unique; if the first element of the list is of a different datatype
* from the second, then the first element will be ignored in creating the
* list but stored as part of the key identifying the list.
*/
/*
* In the case of the shuffle function we'll leverage the library's
* ShuffledList class. The function creates a new ShuffledList
* corresponding to each list it's passed and stores a refence to it in a
* LookupTable. The ShuffledList's getNextValue method is then used to
* return the next value from the list.
*/
shuffle([lst])
{
if(lst.length == 1 && dataType(lst[1]) == TypeList)
lst = lst[1];
local tab = listControl.getTable(&shuffleTab);
local shufList = tab[lst];
local modifiedLst = lst;
if(shufList == nil)
{
/*
* If the first item in the list is of a different type from the
* second, then the first item is simply a marker to make the list
* unique, so we don't want it included in the list of items from
* which a value will be returned.
*/
if(lst.length > 1 && dataType(lst[1]) != dataType(lst[2]))
modifiedLst = lst.cdr();
shufList = new ShuffledList(modifiedLst);
tab[lst] = shufList;
}
return shufList.getNextValue();
}
/*
* The next two functions are very similar, so to avoid duplicated code
* they simple call the appropriate methods on listControl.
*/
stop([lst])
{
if(lst.length == 1 && dataType(lst[1]) == TypeList)
lst = lst[1];
return lst[listControl.getNextIdx(lst, &stopTab, &getNextStopIdx)];
}
cycle([lst])
{
if(lst.length == 1 && dataType(lst[1]) == TypeList)
lst = lst[1];
return lst[listControl.getNextIdx(lst, &cycleTab, &getNextCycleIdx)];
}