/* Copyright (c) 2000 by Kevin Forchione. All Rights Reserved. */ /* * TADS LIBRARY EXTENSION * TMORPH.T * version 3.0 * * tmorph.t provides a simple randomised "text morphing" * mechanism for TADS users. (Text morphing is the production * of randomised print strings). * * * Random Setting * -------------- * * To use this function embed the text morphing syntax within the text * string you wish to display. For example: * * "[|A ball/Balls] of searing flame burst[s/] * out of your magic ring, rebound[s/] off of the ground, and * vaporize[s/] the kni[fe/ves] before [it/they] * can reach you."; * * Place the elements to be morphed within [] brackets. No string * notation should be used inside of the brackets, as everything is * assumed to be text. * * Each separate element of the morphing text should be separated by * the '/' delimeter. If you do not wish to display any text simply * leave the element blank, for example: [silver/gold/] watch." * consists of 3 morphing elements, which would display: * * gold watch * silver watch * watch * * You can and should initialise the random generation at the beginning * of a display sequence by putting the '|' initialiser at the beginning * of the morphing sequence, e.g. [|apple/pear/peach]. The value used * by subsequent text morphing syntax will use the value set by the * initialiser. * * Note that the sequence generated is random unless you specify a * sequencer_name and style in the initialisation section. For example, * * MorphSequencer * -------------- * * [ringseq seq|A ball/Balls/A mighty fireball] * * will dynamically create a sequencer object with the noun property of * 'ringseq'. This object will be initialized with a value of 1, which * means that your text morphing string will begin with #1 in its * morphing element list and sequentially progress each time the * text switch with the initialiser is called. When the end of the list * is reached the last value in the list is displayed from that point * on. In our example the 1st time, 'A ball' is displayed; the 2nd, * 'Balls' displays, and all subsequent displays are 'A mighty * fireball'. * * MorphRandomizer * --------------- * * [ringrnd rnd|A ball/Balls/A mighty fireball] * * will dynamically create a sequencer object with the noun property of * 'ringrnd'. Display is produced in a completely random fashion. * * MorphModulus * ------------ * * [ringmod mod|A ball/Balls/A mighty fireball] * * will dynamically create a sequencer object with the noun property of * 'ringmod'. This object will be initialized with a value of 1, which * means that your text morphing string will begin with #1 in its * morphing element list and sequentially progress each time the text * switch with the initialiser is called. When the end of the list * is reached display starts over from the beginning. * * MorphRedRndSeq * -------------- * * [ringrrs rrs|A ball/Balls/A mighty fireball] * * will dynamically create a sequencer object with the noun property of * 'ringrrs'. This object works with a self-reducing random selection * list that then returns the last displayed element when the list has * been exhausted. * * MorphRedRndMod * -------------- * * [ringmod mod|A ball/Balls/A mighty fireball] * * will dynamically create a sequencer object with the noun property of * 'ringmod'. This object works with a self-reducing random selection * list that then resets the list when the list has been exhausted. * * Dynamic Control * --------------- * * For more dynamic control, based on game-state, you should first set * the value of global.randomHold and remove any initialiser from the * text morph. For example: * * global.randomHold = object.knifeCount; * [A ball/Balls/A mighty fireball] * *---------------------------------------------------------------------- * REQUIREMENTS * * + HTML TADS 2.2.6 or later * + Should be #included after ADV.T and STD.T or WorldClass.t or * pianosa.t * *---------------------------------------------------------------------- * IMPORTANT LIBRARY INTERFACE AND MODIFICATION * * None. * *---------------------------------------------------------------------- * 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 * * 20-Feb-00: Creation. * 21-Feb-00: Modified to include init in the function call. */ #define __TMORPH_MODULE_ #pragma C+ morphFilter: function; formatMorphText: function; tm: function; morphTracker: function; /* * morphFilter: function(str) * * Set this filter in commonInit(). It will continue to pass str to the * formatMorphText() function until the function returns nil. * * setOutputFilter(morphFilter); */ morphFilter: function(str) { local ret; ret = formatMorphText(str); while(ret) { str = ret; ret = formatMorphText(str); } return str; } /* * formatMorphText: function(str) * * This function locates the first occurrence of a "text switch" and * replaces it with the generated switch value from tm(). */ formatMorphText: function(str) { local i, f1, f2, bit, len, val, ret, grp, newstr = ''; // Find the "text switch" ret = reSearch('%[([^%[]*/+[^%[]*)%]', str); if (ret == nil) return nil; f1 = ret[1]; f2 = ret[2]; // Get the information within the brackets grp = reGetGroup(1); bit = grp[3]; // Search for '|' initialiser symbol and set init val ret = reSearch('(.*)%|', bit); if (ret != nil) { grp = reGetGroup(1); if (grp != nil) { if (grp[2] > 0) val = grp[3]; else val = 0; } else val = nil; // strip initialiser symbol and any val from bit bit = substr(bit, ret[2]+1, length(bit)); } // Translate switches into a single value bit = tm(bit, val); // Build new string if (f1 != 1) newstr += substr(str, 1, f1-1); newstr += bit; if (f1+f2 != length(str)) newstr += substr(str, f1+f2, length(str)); return newstr; } /* * tm: function(str, val) * * This function creates a list of single-quote strings from the str * parameter, using '/' as delimiters. It also sets the * global.randomHold used to maintain morphing consistency. */ tm: function(str, val) { local i, s, w = '', list = []; for (i = 1; i <= length(str); ++i) { s = substr(str, i, 1); if (s == '/') { list += w; w = ''; } else w += s; } list += w; if (val == 0) global.randomHold = val; else if (val != nil) global.randomHold = morphTracker(val, length(list)); if (proptype(global, &randomHold) != DTY_NUMBER || global.randomHold < 1) global.randomHold = _rand(length(list)); return list[global.randomHold]; } /* * morphTracker: function(str, len) * * Function creates a dynamic object using the sequence_name provided * by the initialisation area of the text morph. This object is * initialised with val and given the noun vocabulary of the * sequence_name. * * If no val was provided then the object is initialised for position * 1 in the list of morphing elements. */ morphTracker: function(str, len) { local tokenList, typeList, objList, mt, tk, ty, o; tokenList = parserTokenize(str); typeList = parserGetTokTypes(tokenList); tk = tokenList[1]; ty = typeList[1]; if (length(tokenList) == 2) mt = tokenList[2]; else mt = 'rnd'; objList = parserDictLookup([tk], [ty]); if (objList && length(objList)) o = objList[1]; else { switch(mt) { case 'seq': o = MorphSequencer.instantiate(tk, len); break; case 'mod': o = MorphModulus.instantiate(tk, len); break; case 'rrm': o = MorphRedRandMod.instantiate(tk, len); break; case 'rrs': o = MorphRedRandSeq.instantiate(tk, len); break; default: o = MorphRandomizer.instantiate(tk, len); break; } } return o.getVal; } class MorphSequencer: object noun = '' len = 0 val = 0 getVal = { self.val++; if (self.val > self.len) self.val = len; return self.val; } instantiate(tk, l) = { local x = new MorphSequencer; x.len = l; addword(x, &noun, tk); return x; } ; class MorphRandomizer: MorphSequencer getVal = { self.val = _rand(self.len); return self.val; } instantiate(tk, l, ) = { local x = new MorphRandomizer; x.len = l; addword(x, &noun, tk); return x; } ; class MorphModulus: MorphSequencer getVal = { self.val++; if (self.val > self.len) self.val = 1; return self.val; } instantiate(tk, l) = { local x = new MorphModulus; x.len = l; addword(x, &noun, tk); return x; } ; class MorphRedRandMod: MorphSequencer rndList = [] getVal = { local i, ln, n, newList = []; ln = length(self.rndList); if (ln == 0) { // rebuild the rndList ln = self.len; self.rndList = []; for (i = 1; i <= ln; ++i) self.rndList += i; } n = _rand(ln); self.val = self.rndList[n]; for (i = 1; i <= ln; ++i) if (self.rndList[i] != self.val) newList += self.rndList[i]; self.rndList = newList; return self.val; } instantiate(tk, l) = { local x = new MorphRedRandMod; local i; x.len = l; x.rndList = []; for (i = 1; i <= l; ++i) x.rndList += i; addword(x, &noun, tk); return x; } ; class MorphRedRandSeq: MorphSequencer rndList = [] getVal = { local i, ln, n, newList = []; ln = length(self.rndList); if (ln == 0) { return self.val; } n = _rand(ln); self.val = self.rndList[n]; for (i = 1; i <= ln; ++i) if (self.rndList[i] != self.val) newList += self.rndList[i]; self.rndList = newList; return self.val; } instantiate(tk, l) = { local x = new MorphRedRandSeq; local i; x.len = l; x.rndList = []; for (i = 1; i <= l; ++i) x.rndList += i; addword(x, &noun, tk); return x; } ; #pragma C-