| /* | |
| * 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:eric.eve@hmc.ox.ac.uk"> | |
| 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)]; | |
| } | |
Xet Storage Details
- Size:
- 7.35 kB
- Xet hash:
- 6ec0a4220ef7c79dc37902cc3b493d0a0254b77119098ef7cc68955c086b010b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.