/* Copyright (c) 2000 by Kevin Forchione. All Rights Reserved. */ /* * TADS ADV.T/STD.T LIBRARY EXTENSION * AGAIN.T * version 2.0 * * This module implements a solution to the <> bug that * exists in TADS 2. <> is limited by the fact that TADS * repeats the command based on command objects and not player * command. This can cause problems with commands directed toward * indistinguishable objects. * * You see two apples here. * * >eat apple * That was delicious! * >g * You can't repeat that command. * * Again.t remedies this by capturing the command word list * passed to preparseCmd(), then later converting it into a * command string and prefixing it with the actor vocabulary. * * You see two apples here. * * >eat apple * That was delicious! * >g * That was delicious! * >g * I don't see any apple here. * * * NOTE that capturing the actual words used to address the actor * would complicate the solution. There is, however, the possibility * that a parser-generated error message for an "again" command * that refers to an actor might use words not contained in the * command. For example: * * >x baggins * blah blah blah * baggins has left the area. * * >g * There is no bilbo baggins of hobbiton here. * *---------------------------------------------------------------------- * REQUIREMENTS * * + HTML TADS 2.5.0 or later * + Should be compatible with ADV.T, WorldClass, Pianosa * libraries. Alt users will find that version 1.0.6 implements * a similar solution. * + Should be #included after the main library modules. * *---------------------------------------------------------------------- * IMPORTANT LIBRARY INTERFACE AND MODIFICATION * * + preparseCmd() * + preCommand() * + compoundWords - special note, if you add a new compoundWord * you must add an equivalent replaceWith() for it in the * cvtFromCompound() method of the Stringizer class. * *---------------------------------------------------------------------- * 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 * * 06-06-00: Creation. * 16-06-00: Added conversion of compound words. */ #ifndef __AGAIN_MODULE_ #define __AGAIN_MODULE_ #pragma C+ /* * The preparseCmd() function saves all command word lists except for * <> or <> in global.cmdWordList. If the command is * <> or <> we replace it with the stored global command * string. * * It returns true to indicate that processing is to continue with the * command unchanged. */ preparseCmd: function(cmd) { if (length(cmd) == 1 && (cmd[1] == 'g' || cmd[1] == 'again')) { parserReplaceCommand(global.cmdString); } else { global.cmdWordList = cmd; } return true; } /* * The preCommand() function builds the command string that is then * stored on global. */ preCommand: function(actor, verb, doList, prep, iobj) { // Build command string only if command was player generated. if (length(global.cmdWordList)) { local str; str = Stringizer.buildCmdString(actor, global.cmdWordList); global.cmdString = str; } } /* * The Stringizer class converts a word list to a string. In addition * it handles the conversion of TADS special words and the appending of * actor vocabulary to the front of the string to create a command * string. */ class Stringizer: object buildCmdString(actor, cmdWordList) = { local actorWordList, actorString, cmdWordString, cmdString; cmdWordList = self.cvtFromSpec(cmdWordList); cmdWordString = self.stringize(cmdWordList); cmdWordString = self.cvtFromCompound(cmdWordString); /* * NOTE: getActorVocab() assumes that the actor isn't numbered. If * you find yourself having difficulty with this option #define * USE_ACTOR_SDESC. */ #ifdef USE_ACTOR_SDESC actorString = self.getActorSdesc(actor); #else /* USE_ACTOR_SDESC */ actorWordList = self.getActorVocab(actor); actorString = self.stringize(actorWordList); #endif /* USE_ACTOR_SDESC */ cmdString = actorString + ', ' + cmdWordString; return cmdString; } cvtFromSpec(wordList) = { local i, len; len = length(wordList); for (i = 1; i <= len; ++i) { switch(wordList[i]) { case ',': wordList[i] = 'and'; break; case 'A': wordList[i] = 'all'; break; case 'X': wordList[i] = 'but'; break; case 'I': wordList[i] = 'it'; break; case 'T': wordList[i] = 'them'; break; case 'M': wordList[i] = 'him'; break; case 'R': wordList[i] = 'her'; break; case 'Y': wordList[i] = 'any'; break; case 'B': wordList[i] = 'both'; break; case 'N': wordList[i] = 'one'; break; case 'P': wordList[i] = 'ones'; break; } } return wordList; } cvtFromCompound(str) = { str = replaceWith(str, '%', 'on to'); str = replaceWith(str, '%', 'in to'); str = replaceWith(str, '%', 'in between'); str = replaceWith(str, '%', 'down in'); str = replaceWith(str, '%', 'down on'); str = replaceWith(str, '%', 'up on'); str = replaceWith(str, '%', 'out of'); str = replaceWith(str, '%', 'off of'); str = replaceWith(str, '%', 'i wide'); str = replaceWith(str, '%', 'i tall'); str = replaceWith(str, '%', 'wait for'); str = replaceWith(str, '%', 'wait until'); return str; } stringize(wordList) = { local i, len, str = ''; len = length(wordList); for (i = 1; i <= len; ++i) { str += wordList[i]; if (i != len) str += ' '; } return str; } getActorSdesc(actor) = { local stat, str, actorWordList; if (actor == parserGetMe()) { actorWordList = self.getActorVocab(actor); str = self.stringize(actorWordList); } else { stat = outcapture(true); actor.sdesc; str = outcapture(stat); } return str; } getActorVocab(actor) = { local actorWordList = []; actorWordList += getwords(actor, &adjective); if (actor.isThem) actorWordList += getwords(actor, &plural)[1]; else actorWordList += getwords(actor, &noun)[1]; return actorWordList; } ; #ifndef __REPLACEWITH_MODULE_ #define __REPLACEWITH_MODULE_ #define RW_REPLACE_ONCE 1 #define RW_MATCH_WORD 2 #define RW_MATCH_CASE 4 #define RW_RET_NIL 8 /* * replaceWith(value, target, replacement, flags) * * The function searches the value string, replacing the target string * with the replacement string. * * Bit-flags can be passed to control the search. * * RW_REPLACE_ONCE replace only one occurrence of the target in the * value string. * * The default for replaceWith() is to replace all occurrences * of the target in the value string. * * RW_MATCH_WORD target must match whole words. For example: * * target 'get in' will search for '%% *%' * which will match 'get in the chair', but not * 'get into the chair' * * RW_MATCH_CASE target must match the case in value. * * The default for replaceWith() is case-insensitive. A target * string of 'get into' will match on 'GET INTO THE CAR' as * well as 'Get into the car' unless RW_MATCH_CASE is used. * * RW_RET_NIL function returns nil if no match for the target found * * The default for replaceWith() returns the value unchanged. * Using RW_RET_NIL will return the value only if replacement * occurred; otherwise the function will return nil. */ replaceWith: function(value, target, replacement, ...) { local ret, new_value = ''; local valuesave, targetsave, replacementsave; local flags = 0; if (argcount > 3) flags = getarg(4); if ((flags & RW_MATCH_WORD)!= 0) { local tmptarget = '%<'; tmptarget += replaceWith(target, ' ', '%> *%<'); tmptarget += '%>'; target = tmptarget; } do { if ((flags & RW_MATCH_CASE) == 0) { valuesave = value; targetsave = target; replacementsave = replacement; value = lower(value); target = lower(target); replacement = lower(replacement); } ret = reSearch(target, value); if ((flags & RW_MATCH_CASE) == 0) { value = valuesave; target = targetsave; replacement = replacementsave; } if (ret) { local len, tmp = ''; len = length(value) + 1; if (ret[1] - 1) tmp += substr(value, 1, ret[1]-1); tmp += replacement; new_value += tmp; if (len - (ret[1]+ret[2])) value = substr(value, ret[1]+ret[2], len - (ret[1]+ret[2])); else value = ''; if ((flags & RW_REPLACE_ONCE) != 0) break; } else if ((flags & RW_RET_NIL)!= 0 && new_value == '') return nil; } while( ret != nil); new_value += value; return new_value; } #pragma C- #endif /* __REPLACEWITH_MODULE_ */ #endif /* __AGAIN_MODULE_ */