| TADS Revision History - versions 2.2.3 through 2.4.0 | |
| ==================================================== | |
| This file contains a list of changes that have been made to TADS from | |
| version 2.1.0 through version 2.4.0. Most of the changes are fixes | |
| to bugs, so they don't change the documented behavior, but a few, as | |
| explained below, add new functionality to TADS. Releases are listed | |
| with the most recent release first; each release incorporates all new | |
| features and bug fixes of each prior release unless otherwise stated. | |
| Starting with version 2.2.6, these release notes are divided into | |
| two separate files: one for "generic" changes that apply to TADS on | |
| all types of computers and operating systems, and one for changes | |
| that apply only to specific platforms. This file contains the | |
| generic release notes. Platform-specific release notes are in a | |
| separate file: | |
| tadsv240.txt - MS-DOS and Windows changes, 2.2.3 through 2.4.0 | |
| These release notes sometimes refer to the "character-mode" run-time. | |
| This is meant to distinguish the traditional TADS run-time from the | |
| HTML TADS run-time. The traditional TADS run-time has a graphical | |
| user interface on some systems, such as the Macintosh, so it's not | |
| really a character-mode application on those systems; nonetheless, | |
| we still refer to it here as the character-mode run-time simply to | |
| make it clear that we're not talking about the HTML TADS version. | |
| Note that changes from version 2.0 up to 2.2.2 are not included in | |
| this file in order to keep its size under control. Older release | |
| notes are in separate files for each platform. For DOS, revisions | |
| from 2.0 through 2.1.0 are available in the file TADSV200.DOS, and | |
| those from 2.1.1 through 2.2.2 are in TADSV222.DOS. Similar files | |
| are available for other platforms; please refer to your platform- | |
| specific release notes for details. Because these files are quite | |
| large and are static (they are, after all, historical), they're not | |
| included in the normal TADS distributions, but you can download them | |
| from the Interactive Fiction Archive via the internet at | |
| ftp://ftp.gmd.de/if-archive/programming/tads. | |
| ------------------------------------------------------------------------------ | |
| 2.4.0 - May 16, 1999 | |
| - A new built-in function, exitobj, provides a new method for skipping | |
| the remaining processing for a command at any point. This new | |
| function is very similar to the "exit" statement, but differs in one | |
| respect: whereas the "exit" statement terminates all further | |
| processing for a command and skips directly to the fuses and daemons, | |
| "exitobj" skips the remaining processing only for the current object | |
| in a command and proceeds to the next object. | |
| This difference is significant when the player types in a command | |
| involving multiple objects. For example, suppose that you define a | |
| roomAction method in the current room as follows: | |
| roomAction(actor, verb, dobj, prep, iobj) = | |
| { | |
| /* | |
| * when the player touches anything not already in their inventory, | |
| * make it vanish | |
| */ | |
| if (dobj != nil && !dobj.isIn(actor)) | |
| { | |
| "\^<<dobj.thedesc>> disappears in a flash of light!\n"; | |
| dobj.moveInto(nil); | |
| exit; | |
| } | |
| } | |
| Consider the following transcript: | |
| >take ball | |
| The ball disappears in a flash of light! | |
| >take hammer and chisel | |
| The hammer disappears in a flash of light! | |
| The first response makes sense, but the second isn't exactly what you | |
| wanted. The problem is that the "exit" statement tells the parser to | |
| skip processing of any objects other than the current one. | |
| To change this, you can simply change the "exit" statement in the code | |
| listing above to "exitobj". The result will be more sensible: | |
| >take hammer and chisel | |
| The hammer disappears in a flash of light! | |
| The chisel disappears in a flash of light! | |
| "exitobj" is useful when you want to skip the remaining processing for | |
| a command for the current object, but you still want the command to be | |
| considered successful. "exit" is more suited for situations where the | |
| outcome of the command is something less than total success, and you | |
| want to skip further processing of other objects involved in the | |
| command. "exitobj" is particularly useful with the new execCommand() | |
| built-in function (see below), because it allows you to completely | |
| redirect the processing of a command, skipping all or part of the | |
| normal processing for the original command without telling the parser | |
| that the original command was unsuccessful. | |
| You an use exitobj anywhere you can use exit. | |
| - A new built-in function, execCommand, gives a game program direct | |
| access to the parser's command execution system. This new function | |
| doesn't provide direct access to the string-parsing portion of the | |
| parser, but to the command execution portion, which takes the objects | |
| involved in the command and executes the command, performing object | |
| validation (validDo, validIo), room notification (roomAction), | |
| actor notification (actorAction), direct and indirect object checks | |
| (dobjCheck and iobjCheck), general object handling (dobjGen and | |
| iobjGen), object validation (verIoVerb and verDoVerb), and object | |
| action processing (ioVerb, doVerb, or verb.action, as appropriate). | |
| execCommand is called like this: | |
| errorCode := execCommand(actor, verb, dobj, prep, iobj, flags); | |
| The "actor" parameter is the object (usually of class Actor) of the | |
| character to perform the action; if the player character is to carry | |
| out the command, use Me as the "actor" parameter. The "verb" | |
| parameter is the deepverb object of the command to execute. "dobj" | |
| is the direct object of the command, and must be a single object | |
| (not a list); if you want to execute the same command on a series | |
| of direct objects, simply call execCommand in a loop. Use nil for | |
| the "dobj" parameter if the command takes no direct object. "prep" | |
| is the object (usually of class Prep) for the preposition that | |
| introduces the indirect object, or nil if there is no preposition. | |
| "iobj" is the indirect object of the command, or nil if the command | |
| takes no indirect object. | |
| The "flags" parameter lets you control how the parser processes the | |
| command. The flags are "bit field" values, which means that you can | |
| combine any number of the constants below using the bitwise "or" | |
| operator, "|". The constants below are defined in adv.t. | |
| EC_HIDE_SUCCESS - if this flag is included, the parser will hide | |
| any messages that the command generates when the command completes | |
| successfully. The parser considers the command successful if the | |
| return code from execCommand is zero (see below). If this flag is | |
| not included, and the command is successful, all messages will be | |
| displayed. Note that this flag is independent of EC_HIDE_ERROR; | |
| whether or not this flag is included has no effect on messages if | |
| the command is not successful. | |
| EC_HIDE_ERROR - if this flag is included, the parser will hide | |
| any messages that the command generates when the command fails. | |
| The parser considers the command to have failed when the return | |
| code from execCommand is non-zero (see below). If this flag is | |
| not included, and the command fails, all messages will be displayed. | |
| Note that this flag is independent of EC_HIDE_SUCCESS; whether or | |
| not this flag is included has no effect on messages if the command | |
| is successful. | |
| EC_SKIP_VALIDDO - if this flag is included, the parser skips | |
| validation of the direct object. If this flag is not used, the | |
| parser uses the normal validation procedure for the direct object. | |
| You can use this flag when you want to bypass the normal validation | |
| process and execute the command even when the actor would not | |
| normally have access to the direct object. | |
| EC_SKIP_VALIDIO - if this flag is included, the parser skips | |
| indirect object validation. If this flag isn't used, the parser | |
| uses the normal procedure for validating the indirect object. | |
| If you want to execute a command silently, so that the player doesn't | |
| see the messages the command would normally generate, you should | |
| specify both EC_HIDE_SUCCESS and EC_HIDE_ERROR: | |
| err := execCommand(actor, takeVerb, ball, nil, nil, | |
| EC_HIDE_SUCCESS | EC_HIDE_ERROR); | |
| In some cases, you may want to show messages only in case of error. | |
| This is particularly useful when you're executing an implied command, | |
| such as opening a door in the course of moving to a new room, because | |
| you'll usually show a message of some kind noting the implied action. | |
| In this situation, you can use EC_HIDE_SUCCESS to suppress the normal | |
| confirmation message you'd receive on success, but still show any | |
| errors that occur: | |
| "(Opening the door)\n"; | |
| err := execCommand(actor, openVerb, steelDoor, nil, nil, | |
| EC_HIDE_SUCCESS); | |
| if (err = EC_SUCCESS) | |
| { | |
| // successfully opened the door, so proceed with the movement... | |
| } | |
| All of the parameters to execCommand after "verb" can be omitted, | |
| in which case dobj, iobj, and prep will default to nil, and flags | |
| will default to 0 (zero). In addition, you can supply a 'flags' | |
| value but omit one or more of 'dobj', 'prep', and 'iobj', in | |
| which case the interpreter will use nil as the default value for | |
| the omitted object arguments. | |
| execCommand returns an error code indicating the parser's | |
| results. A return value of zero indicates that the command was | |
| processed without any parser errors. A return value of zero does | |
| not always mean that the command did what you wanted -- it merely | |
| indicates that all of the checks succeeded, including xobjCheck, | |
| xobjGen, roomAction, actorAction, verDoVerb, and verIoVerb, and | |
| that no "exit" or "abort" statement was executed. In some cases, | |
| though, the action, doVerb, or ioVerb method will make further | |
| checks and generate an error; in these cases, execCommand will | |
| return zero even though the command failed. You may therefore | |
| want to make an extra check to ensure that whatever state change | |
| you were attempting to accomplish actually occurred. | |
| This function can return the following values (the constants are | |
| defined in adv.t): | |
| EC_SUCCESS | |
| success - the doVerb or ioVerb method was successfully invoked, | |
| and no "exit" or "abort" statement was executed. | |
| EC_EXIT | |
| an "exit" statement was executed. This usually means that a | |
| roomAction, actorAction, xobjCheck, or xobjGen method disallowed | |
| the command. | |
| EC_ABORT | |
| an "abort" statement was executed. | |
| EC_INVAL_SYNTAX | |
| sentence structure is invalid. This indicates that the | |
| combination of verb, objects, and preposition does not form a | |
| valid command in the game. The parser does not display any | |
| error message in this case; this indicates an error in your | |
| source code, since you're attempting to execute a verb | |
| pattern that your game doesn't define. | |
| EC_VERDO_FAILED | |
| verDoVerb failed. The direct object's verification method | |
| displayed some text, indicating that verification failed. | |
| EC_VERIO_FAILED | |
| verIoVerb failed. The indirect object's verification method | |
| displayed text. | |
| EC_NO_VERDO | |
| no verDoVerb method is defined for the direct object. This | |
| is almost equivalent to EC_VERDO_FAILED, but indicates that a | |
| default parser message was displayed, because the object had | |
| no defined or inherited handler of its own. | |
| EC_NO_VERIO | |
| no verIoVerb method is defined for the indirect object. The | |
| parser displays a default message. | |
| EC_INVAL_DOBJ | |
| direct object validation failed. This indicates that that | |
| direct object isn't accessible for the verb; the parser will | |
| display the usual message (via the cantReach mechanism) | |
| before returning. | |
| EC_INVAL_IOBJ | |
| indirect object validation failed. The indirect object is | |
| not accessible for the verb; the parser will display the | |
| usual message before returning. | |
| Note that the parser does not check to see if the actor is present | |
| in the same room with the current actor, or perform any other | |
| addressibility validation for the actor. This allows you to use | |
| execCommand to script non-player character actions to be carried | |
| out autonomously, without regard to whether the player could have | |
| issued the same commands directly. | |
| The current actor, for the purposes of format string ("%xxx%") | |
| evaluation, is set to the "actor" parameter specified in the call. | |
| execCommand does not invoke any daemons or fuses. The recursive | |
| command is considered part of the current turn. | |
| You should not use execCommand in verification methods (verIoVerb or | |
| verDoVerb), because execCommand invokes complete commands, which may | |
| make changes in game state. Note that changes in game state will | |
| occur regardless of EC_HIDE_SUCCESS or EC_HIDE_ERROR - these flags | |
| merely hide the messages the command produces, but don't prevent | |
| the command from carrying out its other actions. | |
| One good use for execCommand is to treat different phrasings of a | |
| command the same way. For example, suppose you had a can of spray | |
| paint in your game. You might want to allow players to paint things | |
| with the spray paint using commands like "spray <paint> on <target>" | |
| and "spray <target> with <paint>." To make these commands equivalent, | |
| you traditionally would have coded the verIoSprayOn, verDoSprayOn, | |
| ioSprayOn, and doSprayOn routines appropriately, then essentially | |
| duplicated the code in the SprayWith equivalents. Alternatively, you | |
| could have set up the SprayWith routines to call the SprayOn routines | |
| (or vice versa); this kind of indirection is tricky, though, because | |
| of the TADS parser's assymetrical handling of direct and indirect | |
| object verification -- note that the direct and indirect objects | |
| reverse roles between the two phrasings. | |
| This kind of redirection is easy using execCommand, though. First, | |
| choose a "canonical" phrasing -- this is the phrasing where you'll | |
| always implement your handlers for the command. Let's choose | |
| "spray <paint> on <target>" as the canonical phrasing. We now set | |
| up command handlers for our canonical phrasing just as we would for | |
| any other command: we'd write verIoSprayOn and ioSprayOn methods for | |
| objects that we want to allow as targets for spraying, and we'd write | |
| verDoSprayOn methods for objects that can do the spraying. For | |
| example, in the sprayPaintCan object, we'd write a verDoSprayOn | |
| handler to allow spraying the paint on things: | |
| sprayPaintCan: item | |
| // put your sdesc, ldesc, vocabulary, etc. here | |
| verDoSprayOn(actor, iobj) = { } | |
| ; | |
| We'd proceed with the normal implementation of "spray <paint> on | |
| <target>" until that worked correctly. Once the canonical phrasing | |
| worked, we'd set up the redirection. Rather than setting up a | |
| complicated series of method-by-method redirections, we can simply | |
| allow any "spray <target> with <paint>" command to proceed all the | |
| way to the ioSprayWith handler, then redirect the entire command at | |
| that point. Since we want to redirect the command for every pair of | |
| objects, we can put all of the handlers in the basic "thing" object: | |
| modify thing | |
| /* allow ANY "spray <target> with <object>" command */ | |
| verIoSprayWith(actor) = { } | |
| verDoSprayWith(actor, iobj) = { } | |
| /* re-cast "spray <self> with <iobj>" as "spray <iobj> on <self> */ | |
| ioSprayWith(actor, dobj) = | |
| { | |
| execCommand(actor, sprayVerb, self, onPrep, dobj); | |
| } | |
| ; | |
| That's all that we need to do -- because execCommand will run through | |
| the entire parsing sequence for the new phrasing, we don't need to | |
| worry about doing any verification for the non-canonical phrasing. | |
| Note that we must put the execCommand call in ioSprayWith, and not | |
| in one of the verXoSprayWith methods -- if we put the call in one of | |
| the verification methods, we could execute the recursive command | |
| multiple times in silent calls during disambiguation. Note also that | |
| we can override the equivalence of "spray <x> on <y>" and "spray <y> | |
| with <x>" on an object-by-object basis, if we wanted, by overriding | |
| the SprayWith methods in the objects where we wanted different | |
| behavior; while the "spray" commands may never need such special | |
| handling, other equivalencies might benefit: "put <x> in <y>" and | |
| "fill <y> with <x>," for example, might only be equivalent for | |
| liquids and containers of liquids. | |
| Another use for execCommand is to perform "implied" commands -- these | |
| are commands that the game carries out automatically, without the | |
| player specifically typing them in, because they're obviously needed | |
| in the course of what the player actually did type. | |
| As an example, suppose you want the player to be wearing sunglasses | |
| every time they enter a particular room. You could simply check to | |
| see if the player is wearing the sunglasses, and forbid travel into | |
| the room if not: | |
| >north | |
| You can't enter the fusion chamber without your sunglasses on. | |
| >wear sunglasses | |
| You're now wearing the sunglasses. | |
| >north | |
| This would work, but it's tedious for the player, in that the game | |
| tells the player exactly what to type, but still makes the player | |
| type it. Some people would still prefer to believe (despite evidence | |
| to the contrary) that computers are our servants and not our masters, | |
| and tend to balk at this type of laziness on the part of the game. | |
| Even more tedious, though, was writing the code traditionally necessary | |
| to make this operation automatic. The problem is that you'd have had | |
| to write code to make all of the same checks that the parser would | |
| normally make to find out if wearing the sunglasses is possible, and | |
| also make sure that any side effects are invoked. | |
| execCommand makes this kind of operation easy, by allowing you to | |
| use exactly the same code that the parser would invoke in order to | |
| carry out an explicit command from the player. In effect, this | |
| lets you automatically run obviously implied commands, rather than | |
| telling the player to run them manually. Here's how we might use | |
| this for the sunglasses: | |
| outsideChamber: room | |
| // normal sdesc/ldesc stuff | |
| north = | |
| { | |
| /* if the sunglasses aren't being worn, try putting them on */ | |
| if (sunglasses.isIn(parserGetObj(PO_ACTOR)) && !sunglasses.isworn) | |
| { | |
| /* | |
| * The sunglasses are here but not worn - put them on. | |
| * Tell the player what we're doing, then execute a "wear" | |
| * command recursively. Note that we use EC_HIDE_SUCCESS | |
| * in the execCommand call to suppress the normal success | |
| * confirmation message - we only want a message if the | |
| * player can't wear the sunglasses for some reason. | |
| */ | |
| "(First wearing the sunglasses)\n"; | |
| if (execCommand(parserGetObj(PO_ACTOR), wearVerb, sunglasses, | |
| nil, nil, EC_HIDE_SUCCESS) != 0) | |
| { | |
| /* | |
| * that failed - since execCommand showed error messages, | |
| * we have already explained what went wrong, so simply | |
| * return nil to disallow the travel | |
| */ | |
| return nil; | |
| } | |
| } | |
| /* if they're not wearing eye protection, don't allow entry */ | |
| if (!(sunglasses.isIn(parserGetObj(PO_ACTOR)) | |
| && sunglasses.isworn)) | |
| { | |
| /* explain the problem */ | |
| "%You% venture%s% a few steps, but the light in the chamber | |
| is so intense that %you're% forced to retreat. %You%'ll | |
| need some sort of eye protection to enter. "; | |
| /* don't allow travel */ | |
| return nil; | |
| } | |
| /* the sunglasses are deployed - we're good to go */ | |
| return fusionChamber; | |
| } | |
| ; | |
| Note that this example uses the new parserGetObj() built-in function | |
| (described below), rather than parserGetMe(), to determine which actor | |
| is performing the travel. In addition, the messages all use format | |
| strings (such as "%You%"). These two elements ensure that the travel | |
| method can be used for travel by the player, but also can be used for | |
| travel by a non-player character; this is especially important if you | |
| plan to use execCommand() to script NPC actions, since the current | |
| actor during recursive commands sent to an NPC would reflect the | |
| NPC object, rather than the player character ("Me") object. | |
| - A new action method gives the game program a chance to perform special | |
| handling on a command after the objects involved have been resolved, | |
| but before any of the usual parser processing begins. This new hook | |
| is similar to preparse() and preparseCmd(), but takes as parameters | |
| the objects involved in the command rather than the original text. | |
| The new hook is a method named verbAction. This new method is similar | |
| to the roomAction and actorAction methods, but gives the deepverb | |
| object a chance to act. The parser calls this method like this: | |
| verb.verbAction(actor, dobj, prep, iobj) | |
| 'actor' is the object representing the actor to whom the command is | |
| addressed; 'dobj' is the direct object, or nil if there is no direct | |
| object; 'prep' is the object for the preposition introducing the | |
| indirect object; and 'iobj' is the indirect object, or nil if there | |
| is no indirect object. | |
| This method returns no value. If this method wants to cancel | |
| execution of the command, it should use the "exit" statement (to | |
| continue execution of any remaining commands on the command line, | |
| but terminate processing of the current command) or the "abort" | |
| statement (to terminate processing of the current command line | |
| entirely). If can also use "exitobj" to terminate processing of | |
| the current object only, and continue execution with the next | |
| object of a multi-object command. | |
| The parser calls verbAction just before calling actorAction. | |
| The verbAction method can be used in conjunction with the new | |
| execCommand built-in function to substitute one sentence pattern | |
| for another, without the usual complexity of mapping several | |
| individual verification and action methods. For example, if you | |
| wanted to process all "fill x with y" commands as "put y in x" | |
| commands, you could put this in your fillVerb definition: | |
| fillVerb: deepverb | |
| verb = 'fill' | |
| sdesc = "fill" | |
| ioAction(withPrep) = 'FillWith' | |
| verbAction(actor, dobj, prep, iobj) = | |
| { | |
| /* check for fill-with syntax */ | |
| if (prep = withPrep) | |
| { | |
| /* handle "fill x with y" exactly like "put y in x" */ | |
| execCommand(actor, putVerb, iobj, inPrep, dobj); | |
| /* we're done with the command - do not process it further */ | |
| exitobj; | |
| } | |
| } | |
| ; | |
| - A new parser hook lets the game take control whenever the parser | |
| encounters an unknown verb, or unknown sentence syntax. The new | |
| hook is a function called parseUnknownVerb, which is defined like | |
| this: | |
| parseUnknownVerb: function(actor, wordlist, typelist, errnum) | |
| 'actor' is the current actor object. The 'wordlist' parameter is a | |
| list with the strings of the words in the command, in the same format | |
| as the list that is passed to preparseCmd. The 'errnum' parameter is | |
| the parser error number for the condition that caused the call to | |
| parseUnknownVerb; this is the same error number that is passed to | |
| parseError (and related functions). | |
| The 'typelist' argument is a list of the types of the words in the | |
| 'wordlist' parameter. Each element of 'typelist' gives the word type | |
| of the corresponding element of 'wordlist' (so typelist[3] gives the | |
| type of the word in wordlist[3], for example). Each type is a number, | |
| which can contain any number of the values below combined with the | |
| bitwise OR operator ("|"). To test for a particular type, use an | |
| expression like this: ((typelist[3] & PRSTYP_NOUN) != 0). The | |
| type values, defined in adv.t, are: | |
| PRSTYP_ARTICLE - the word is defined as an article | |
| PRSTYP_ADJ - adjective | |
| PRSTYP_NOUN - noun | |
| PRSTYP_PLURAL - plural | |
| PRSTYP_PREP - preposition | |
| PRSTYP_VERB - verb | |
| PRSTYP_SPEC - special word (".", "of", "and", etc.) | |
| PRSTYP_UNKNOWN - the word is not in the dictionary | |
| This function can return, true, nil, or a number, or it can use | |
| "abort" to abort the command. | |
| Returning true indicates that the function has successfully handled | |
| the entire command itself; the parser does not display any error | |
| messages, it executes fuses and daemons as normal, and it proceeds to | |
| continue parsing any remaining text on the command line (after a | |
| period or "then"). | |
| Returning a number (greater than zero) indicates success, just as true | |
| does, but also indicates that the function parsed only the words | |
| before the returned index, and that the remaining words (starting with | |
| the word at the index value returned) are to be considered an | |
| additional command. The parser will run fuses and daemons as normal, | |
| and then will resume its normal parsing, starting with the word at the | |
| index given by the return value. You can use this if you find "and" | |
| following a noun phrase that you parse, or if for any other reason you | |
| find that another sentence follows and should be parsed separately. | |
| For example, if you succesfully parse the first three words of the | |
| list, you should return the value 4 to indicate that you want the | |
| parser to apply the default parsing starting with the fourth word in | |
| the list. | |
| Returning nil indicates that the function has failed to handle the | |
| command, and wants the parser to display the default error message. | |
| The parser will display the message in the normal fashion, using | |
| parseErrorParam or parseError as appropriate, and will abort the | |
| command. No fuses or daemons will be executed, and any remaining | |
| text on the command line will be discarded. | |
| If this function uses "abort" to end the command, the parser will | |
| not execute any fuses or daemons, and it will ignore any remaining | |
| text on the command line. The difference between returning nil and | |
| executing an "abort" statement is that the parser will display the | |
| default message when this function returns nil, but will not display | |
| anything if the function uses "abort". | |
| The parseUnknownVerb function is currently called with the following | |
| error codes: | |
| 17 There's no verb in that sentence! | |
| 18 I don't understand that sentence. | |
| 19 There are words after your command I couldn't use. | |
| 20 I don't know how to use the word "%s" like that. | |
| 21 There appear to be extra words after your command. | |
| 23 internal error: verb has no action, doAction, or ioAction | |
| 24 I don't recognize that sentence. | |
| Error code 17 indicates that the first word in the sentence is not | |
| defined as a verb (which may mean that the word is entirely uknown, or | |
| that it's defined as another part of speech but not as a verb). 18 | |
| means that a noun phrase was not formed properly, or that the | |
| combination of the verb and verb preposition ("pick up," for example) | |
| is not defined. 19 means that a preposition occurred at the end of a | |
| sentence, but it could not be combined with the verb. 20 indicates | |
| that the word separating the indirect object is not defined as a | |
| preposition. 21 means that another word follows what the parser | |
| thinks should be the last word in the sentence (for example, the | |
| sentence ends with two prepositions). 23 means that the deepverb | |
| object has no defined templates (action, doAction, or ioAction). 24 | |
| indicates that too many objects were used with the sentence: a direct | |
| object is present, but the deepverb doesn't have a doAction, or an | |
| indirect object is present, but the deepverb doesn't have an ioAction. | |
| The purpose of this function is to let you defined your own parsing | |
| for commands outside of the bounds of the built-in parser. Although | |
| this function is similar to preparseCmd, it differs in that | |
| parseUnknownVerb runs only when the parser can't handle a command | |
| directly, which means that parseUnknownVerb doesn't have to decide | |
| whether or not to pass the command to the parser. In addition, | |
| parseUnknownVerb integrates into the turn-handling mechanism, in | |
| that it can control fuse and daemon execution as well as the handling | |
| of remaining text on the command line. | |
| - A new parser hook lets the game program parse noun phrases and perform | |
| the initial resolution to possible matching objects. The parser now | |
| calls a game-defined function named parseNounPhrase, which is defined | |
| like this: | |
| parseNounPhrase: function(wordlist, typelist, current_index, | |
| complain_on_no_match, is_actor_check) | |
| The parameter 'wordlist' is a list of strings, where each string is a | |
| token in the player's command. This is the same type of list that | |
| preparseCmd receives. | |
| 'typelist' is a list of word types for the tokens in the list. The | |
| types are bit flag values, so each element may have multiple types | |
| combined with OR. To test to see if a particular type flag is set, | |
| use the bitwise AND operator, "&"; for example, to test the second | |
| element to determine if it's a noun, use this expresion: | |
| ((typelist[2] & PRSTYP_NOUN) != 0) | |
| The type flag values, defined in adv.t, are: | |
| PRSTYP_ARTICLE - the word is defined as an article | |
| PRSTYP_ADJ - adjective | |
| PRSTYP_NOUN - noun | |
| PRSTYP_PLURAL - plural | |
| PRSTYP_PREP - preposition | |
| PRSTYP_VERB - verb | |
| PRSTYP_SPEC - special word (".", "of", "and", etc.) | |
| PRSTYP_UNKNOWN - the word is not in the dictionary | |
| 'current_index' is the index in the word list of the start of the | |
| noun phrase. This function can look at the previous words in the | |
| list if desired, but the parser has already determined that words | |
| before 'current_index' are part of the verb or of another part of | |
| the command. This function should start parsing at 'current_index'. | |
| 'complain_on_no_match' is a boolean value (true or nil) indicating | |
| whether the function should display an error message if the noun | |
| phrase has no matching objects. If this parameter is true, you should | |
| display an appropriate message on this type of error; otherwise, you | |
| should not display any message in this case. You should always | |
| display an error if the noun phrase is not syntactically correct; the | |
| 'complain_on_no_match' parameter applies only to error messages for | |
| syntactically correct noun phrases that don't refer to any objects. | |
| 'is_actor_check' is a boolean value indicating whether the function | |
| is being called to check for an actor noun phrase. When this is | |
| true, the function should not allow syntax that is obviously | |
| inappropriate for an actor, such as the word "all," or a string or | |
| number; in such cases, the function should simply return an empty | |
| object list to indicate that no valid actor is present. | |
| This function can do one of four things: it can parse the noun phrase, | |
| and return a list of matching objects; it can determine that no noun | |
| phrase is present; it can indicate that a noun phrase is present but | |
| contains a syntax error; or it can let the parser perform the default | |
| noun phrase parsing. | |
| If this function parses a noun phrase successfully, it should return a | |
| list. The first element of the list must be a number, which is the | |
| index of the next token in the word list after the noun phrase. For | |
| example, if 'current_index' is 3 when the function is called, and the | |
| noun phrase consists of one word, the first element of the returned | |
| list should be 4. This tells the parser where it should resume | |
| parsing. The remaining elements of the list are pairs of elements; | |
| the first of each pair is a game object matching the noun phrase, and | |
| the second is a number giving flags for the object. At this point, | |
| it's not necessary to determine whether or not the objects are | |
| accessible, reachable, visible, or anything else; the parser will | |
| disambiguate the list later, when it knows more about the sentence | |
| structure. For now, the routine can simply return a list of all of | |
| the objects that match the vocabulary words. | |
| Multiple flag values can be combined with the bitwise OR operator, | |
| "|". The flags, defined in adv.t, are: | |
| PRSFLG_ALL - the entry is for the word "all" or equivalent | |
| PRSFLG_EXCEPT - the entry is excluded from the "all" list | |
| PRSFLG_IT - the entry matched the pronoun "it" | |
| PRSFLG_THEM - the entry matched the pronoun "them" | |
| PRSFLG_HIM - the entry matched the pronoun "him" | |
| PRSFLG_HER - the entry matched the pronoun "her" | |
| PRSFLG_NUM - the entry is a number | |
| PRSFLG_STR - the entry is a string | |
| PRSFLG_PLURAL - the entry matched a plural usage | |
| PRSFLG_COUNT - the entry has a numeric count as the first word | |
| PRSFLG_ANY - the entry was qualified with "any" | |
| PRSFLG_UNKNOWN - the entry contains an unknown word | |
| PRSFLG_ENDADJ - the entry ends with an adjective | |
| PRSFLG_TRUNC - the entry uses a truncated word | |
| Some examples might be helpful, since the return value is complicated. | |
| For "all" and for the pronouns (it, him, her, them), you should return | |
| a list containing nil as the object, and the appropriate flag value. | |
| For example, if the noun phrase is simply the word "everything", you | |
| would return this (assuming that the index of the word "everything" | |
| was 2): | |
| [3 nil PRSFLG_ALL] | |
| Similarly, if the noun phrase was simply "her", you would return: | |
| [3 nil PRSFLG_HER] | |
| The construction "all except" is also special. You would return a | |
| list whose first entries are nil and PRSFLG_ALL, just as though you | |
| were parsing a simple "all" phrase, but then you'd add entries for all | |
| of the items in the "except" list, with each additional entry's flag | |
| including PRSFLG_EXCEPT. For example, if the player typed "take all | |
| except book and candle", you might return something like this: | |
| [3 nil PRSFLG_ALL book PRSFLG_EXCEPT candle PRSFLG_EXCEPT] | |
| Strings and numbers work the same way: return nil for the object, | |
| and set the appropriate flag. For example, if the player typed | |
| "type 'hello' on keypad", you'd return this: | |
| [3 nil PRSFLG_STR] | |
| If you encounter an unknown word in the noun phrase, and you want to | |
| let the parser resolve the unknown word using its normal mechanisms, | |
| you should return a nil object with the PRSFLG_UNKNOWN flag set: | |
| [4 nil PRSFLG_UNKNOWN] | |
| If you simply want to return a list of objects that match the noun | |
| phrase, it's easy: | |
| [4 book 0 candle 0] | |
| The parser also lets you omit the flags entirely, if you don't need to | |
| include any flags with an object. If an element that follows an | |
| object is another object (or nil), the parser will assume that the | |
| flag value for the preceding object is zero. So, for the example | |
| above, this list is equivalent: | |
| [4 book candle] | |
| If the function parses a noun phrase successfully, but can find no | |
| objects matching the words in the noun phrase (in other words, the | |
| words form a valid noun phrase syntactically, but don't actually refer | |
| to any object in the game), it should return a list that contains only | |
| the index of the next word after the noun phrase. | |
| If the function determines that a noun phrase appears to be present, | |
| but is not syntactically correct, you should display an error message | |
| and return PNP_ERROR. You should not display an error if it doesn't | |
| look like a noun phrase is present at all; instead, you should simply | |
| return a list consisting of the original 'current_index' value to | |
| indicate that you didn't parse any words at all. You should only | |
| display an error and return PNP_ERROR if you determine that a noun | |
| phrase is actually present, but is syntactically incorrect. | |
| If your function determines that it doesn't want to parse the noun | |
| phrase after all, it should simply return PNP_USE_DEFAULT. This tells | |
| the parser to proceed with the default parsing for the noun phrase. | |
| The default noun phrase parser (built in to the TADS interpreter) does | |
| not attempt to resolve or disambiguate objects at this stage. | |
| Instead, it simply creates a list of all of the objects that match | |
| every word in the noun phrase. The reason that the parser doesn't try | |
| to resolve the objects at this stage is that the parser doesn't have | |
| enough information when this routine is called. So, the parser merely | |
| determines the syntactic structure of the noun phrase, and ensures | |
| that at least one object in the game can match all of the words; | |
| later, after the parser has fully analyzed the sentence structure and | |
| knows the verb, prepositions, and number of objects, the parser | |
| resolves and disambiguates the noun phrase. If you write an | |
| implementation of this function, keep this design in mind. | |
| In most cases, you will not want to write a function that completely | |
| replaces the built-in noun phrase parser. Instead, you'll probably | |
| want to check for special cases. When you see a special case, you | |
| should perform your parsing and return an appropriate object list; | |
| when you don't see a special case, you should simply return | |
| PNP_USE_DEFAULT to use the default parsing routine. | |
| - A new parser hook allows the game program to control the disambiguation | |
| process, wherein noun phrases are resolved to specific game objects. | |
| The parser calls two new methods: dismabigDobj, to disambiguate direct | |
| objects; and disambigIobj, for indirect objects. These new methods | |
| are defined on a deepverb object. | |
| The parser calls disambigDobj as follows: | |
| verb.disambigDobj(actor, prep, iobj, verprop, | |
| wordlist, objlist, flaglist, | |
| numWanted, isAmbig, silent); | |
| The parser calls this routine, if defined, most times it resolves a | |
| noun phrase to a concrete list of objects, whether or not the phrase | |
| is ambiguous. This allows you to perform special resolution on | |
| specific noun phrases or for specific verbs, even when the parser | |
| wouldn't normally think the phrases require disambiguation. | |
| The 'actor' parameter is the actor involved in the command. | |
| 'prep' is the preposition object associated with the word that | |
| introduces the indirect object, if present; if there is no | |
| preposition, 'prep' will be nil. | |
| 'iobj' is the indirect object, if available; if there is no indirect | |
| object, or the indirect object has not yet been resolved when | |
| disambigDobj is invoked, 'iobj' will be nil. | |
| 'verprop' is the property address of the verification method | |
| (verDoVerb) defined for the direct object for the verb; this parameter | |
| is included because a single verb object could define several | |
| verification methods that very by preposition ("put x in y" usually | |
| has a different verification method than "put x on y"). | |
| 'wordlist' is a list of strings giving the tokens of the player's | |
| command that make up the noun phrase. This is the same type of list | |
| that preparseCmd receives. | |
| 'objlist' is a list of the objects that the parser found with | |
| dictionary entries matching all of the words in the word list. | |
| 'flaglist' is a list of numbers giving flags for the corresponding | |
| 'objlist' entries; for example, flaglist[3] gives the flags associated | |
| with objlist[3]. | |
| The flag values for 'flaglist' are defined in adv.t. Thee flags are | |
| bit-field values, so multiple flags can be combined with OR into a | |
| single value. To test if a particular flag is set, use the bitwise | |
| AND operator, "&"; for example, to test the second element to see if | |
| the PLURAL flag is set, use this expression: | |
| ((flaglist[2] & DISAMBIG_PLURAL) != 0) | |
| The flags are: | |
| PRSFLG_COUNT - the object matched with a numbered count. For | |
| example, if the noun phrase is "3 gold coins," objlist will contain | |
| one or more objects matching the plural phrase "gold coins," and the | |
| VOCS_COUNT flag will be set for each object to indicate that a count | |
| is present. In these cases, the first element of wordlist should | |
| always be the string with the user's number (the string '3' in the | |
| example). | |
| PRSFLG_PLURAL - the object matched a plural usage. | |
| PRSFLG_ANY - the noun phrase started with "any" | |
| PRSFLG_ENDADJ - the object matched with an adjective at the end of | |
| the noun phrase. For example, suppose the noun phrase is "letter", | |
| and the game defines parchmentLetter with noun = 'letter', and | |
| defines letterOpener with adjective = 'letter'. In this case, | |
| objlist would contain both parchmentLetter and letterOpener, and the | |
| flaglist entry corresponding to letterOpener would have the | |
| PRSFLG_ENDADJ flag set. This flag allows the disambiguator to select | |
| in favor of the noun interpretation in case of ambiguity, to avoid | |
| having to ask a stupid disambiguation question ("which letter do you | |
| mean, the parchment letter, or the letter opener?" should clearly | |
| not be asked). | |
| PRSFLG_TRUNC - the object matched with one or more truncated words. | |
| This will be set when a word in the player's noun phrase matches the | |
| leading substring of a dictionary word, and is at least six characters | |
| long, but doesn't match the entire dictionary word. For example, | |
| "flashlig" matches "flashlight" because "flashlig" is at least six | |
| characters long and matches "flashlight" in its first eight characters | |
| (i.e., the length of "flashlig"), but the parser will flag the word | |
| with PRSFLG_TRUNC to indicate that it wasn't an exact match. | |
| 'numWanted' is the number of objects that the parser wants in the | |
| resolved list. For a definite singular noun phrase ("take the box"), | |
| this will be 1. For a plural noun phrase, this will be the number | |
| of objects in the proposed resolution list (in the 'objlist' parameter). | |
| When the player specifies a count ("take 3 boxes"), this will be the | |
| give number. For an "any" phrase ("take any box"), this will be 1. | |
| You don't have to obey the 'numWanted' parameter, but this information | |
| may be helpful in some cases. | |
| 'isAmbig' is true if the parser thinks the noun phrase is ambiguous, | |
| nil if not. For example, if the player specified a singular definite | |
| noun phrase, but the parser found two matching objects, 'isAmbig' will | |
| be true. You can always deduce whether or not the list is ambiguous | |
| by examining the 'numWanted' value and all of the object flags, but | |
| doing so is complicated, so the parser provides 'isAmbig' for your | |
| convenience. If your disambigDobj or disambigIobj is interested only | |
| in resolving ambiguity, as opposed to performing special noun phrase | |
| resolution, you can simply return DISAMBIG_CONTINUE immediately if | |
| 'isAmbig' is nil. | |
| 'silent' specifies whether the disambiguation is in interactive or | |
| non-interactive mode. If 'silent' is true, it means that you should | |
| not display any messages or ask the player to help resolve any | |
| ambiguity. If 'silent' is nil, you can display messages or prompt | |
| for more information if you wish. | |
| The disambigIobj method is essentially the same: | |
| verb.disambigIobj(actor, prep, dobj, verprop, | |
| wordlist, objlist, flaglist, | |
| numWanted, isAmbig, silent); | |
| The only difference from disambigDobj is that disambigIobj receives | |
| the direct object in the 'dobj' parameter. Note that the direct | |
| object is not normally available during indirect object | |
| disambiguation, so 'dobj' will usually be nil. The only time the | |
| direct object will be available will be for verb templates with the | |
| [disambigDobjFirst] flag. For verbs without this flag, the indirect | |
| object is resolved before the direct object, hence the direct object | |
| is not yet known. | |
| The parser's built-in disambiguation routine calls these methods after | |
| it has done everything it can, short of asking the player, to resolve | |
| and disambiguate a noun list. The parser will apply the normal | |
| validation checks (validDo, validDoList, etc.) to the objects, | |
| eliminating any that don't pass; and it will apply the silent | |
| verification checks (verDoVerb, verIoVerb) as well. The parser will | |
| next call disambigDobj or disambigIobj, if the verb object defines it. | |
| These methods can return a status code from the list below, or they | |
| can return a list (see below). The status codes (defined in adv.t) | |
| are: | |
| DISAMBIG_CONTINUE - continue through the remainder of the | |
| disambiguation process as normal. | |
| DISAMBIG_ERROR - abort the current command entirely. This can be | |
| used when the method encounters an error and displays an error | |
| message to the player. The parser will simply terminate the current | |
| command. Note that the parser does not display an error of its own, | |
| so the method must display an appropriate message before returning | |
| this status code. | |
| DISAMBIG_PROMPTED - continue through the process as normal, but do | |
| not show a prompt for an interactive response ("which foo do you | |
| mean..."), because the disambigXobj function already displayed an | |
| appropriate prompt of its own. This allows the disambigXobj | |
| function to display a customized prompt (using more information | |
| than the traditional parseDisambig function has available), but | |
| still use the parser's default response reader and parser. | |
| DISAMBIG_PARSE_RESP - this indicates that your code has asked the | |
| player a question and read a response (via the input() built-in | |
| function, for example), but that you want to use the default | |
| response parser in the normal disambiguation mechanism to parse | |
| the response. Do not return this as a raw status code; instead, | |
| return a list containing this value as the first element, and the | |
| string to be parsed as the second element. | |
| DISAMBIG_DONE - consider the object list fully resolved. This skips | |
| any additional checking the disambiguator would normally perform and | |
| uses the current list as-is. This should generally never be | |
| returned directly, but is used when returning a list of objects. | |
| The method can also return a list, which contains objects that replace | |
| the original input list (in the 'objlist' parameter). The first | |
| element of the returned list is a status code, from the list above. | |
| Subsequent elements are the objects to use as the result of | |
| disambiguation. | |
| You can optionally specify a flag value for each object value. To | |
| specify a flag for an object, simply place the flag value after the | |
| object in the list. | |
| return [DISAMBIG_DONE redBox PRSFLG_PLURAL blueBox PRSFLG_PLURAL]; | |
| Each flag value pertains to the object immediately preceding it. | |
| If you omit a flag value, zero is assumed. So, the following two | |
| return lists are equivalent: | |
| [DISAMBIG_CONTINUE redBox 0 blueBox 0] | |
| [DISAMBIG_CONTINUE redBox blueBox] | |
| You can optionally omit the status code from the list. If the first | |
| element of the list is an object, the parser uses DISAMBIG_CONTINUE as | |
| the default status code, so it takes the list you returned and | |
| proceeds to the next step in the disambiguation process. | |
| Note that the parser doesn't call disambigDobj or disambigIobj for | |
| certain cases: resolving pronouns (it, her, him, them); strings; | |
| numbers that don't map to vocabulary words, but simply resolve to | |
| numObj; and "all" phrases. However, the parser does call these | |
| routines to resolve the items in an "except" phrase. | |
| - A new built-in function, parserGetObj, lets the game program learn | |
| the objects involved in the current command. This function takes a | |
| single argument, which indicates which object you want, using the | |
| following constants (defined in adv.t): | |
| PO_ACTOR - the current command's actor | |
| PO_VERB - the deepverb object for the command's verb | |
| PO_DOBJ - the direct object | |
| PO_PREP - the preposition object introducing the indirect object | |
| PO_IOBJ - the indirect object | |
| The return value is an object, or nil if there is no such object | |
| for the current command. For example, a command with no indirect | |
| object will return nil for PO_PREP and PO_IOBJ. | |
| Here's an example of using parserGetObj to get the direct object | |
| of the current command: | |
| local obj := parserGetObj(PO_DOBJ); | |
| parserGetObj returns valid information at any time from (and including) | |
| actorAction to (and including) the doVerb or ioVerb routines. You | |
| can't use parserGetObj() outside of these bounds; if you do, it will | |
| simply return nil. The reason for the limited lifetime of this | |
| function is that the parser simply doesn't know the final values | |
| for the command objects before actorAction, since it is still busy | |
| resolving the words in the command to objects until it's about to | |
| call actorAction. Note some particular times when you can't call | |
| parserGetObj: in the "init" and "preinit" functions; during object | |
| disambiguation (thus during some verDoVerb and verIoVerb calls); | |
| during roomCheck; during preparse() and preparseCmd(); and during | |
| fuse and daemon execution. | |
| Note one important exception to the limited lifetime: the actor | |
| can be retrieved at any time after the preparse() function returns. | |
| The parser determines the actor very early in the process of | |
| interpreting the command, so the actor is available throughout | |
| the parsing sequence. | |
| parserGetObj returns information on the current command. When a | |
| recursive command is in progress (using execCommand), parserGetObj | |
| returns information on the recursive command; once execCommand | |
| finishes and returns to the code that called it, parserGetObj will | |
| once again return information on the enclosing command. | |
| - A new built-in function lets the game invoke the parer's tokenizer to | |
| obtain a token list for a string of text. You can call this new | |
| function like so: | |
| tokenList := parserTokenize(commandString); | |
| The 'commandString' parameter is any string of text. The tokenizer | |
| will scan the string and break it up into tokens, and return a list of | |
| token strings. The token strings follow the same rules as the token | |
| list passed to the preparseCmd() function. | |
| If the command string contains any invalid characters (such as | |
| punctuation marks that the tokenizer doesn't accept), it will return | |
| nil, but it won't display any error messages. | |
| - A new built-in function lets you obtain a list of token types given a | |
| list of tokens. You can use this function to get the types of the | |
| tokens in a list returned by parserTokenize(), for example. Call the | |
| new function like this: | |
| typeList := parserGetTokTypes(tokenList); | |
| The result is a list of numbers. Each element of the result list | |
| gives the type of the corresponding element of the token list | |
| (typeList[3] contains the type of the token in tokenList[3], for | |
| example). | |
| The types in the result list are combinations of the following | |
| values, defined in adv.t: | |
| PRSTYP_ARTICLE - article (a, an, the) | |
| PRSTYP_ADJ - adjective | |
| PRSTYP_NOUN - noun | |
| PRSTYP_PREP - preposition | |
| PRSTYP_VERB - verb | |
| PRSTYP_SPEC - special word | |
| PRSTYP_PLURAL - plural | |
| PRSTYP_UNKNOWN - unknown word | |
| These type codes are bit-field values, so they can be combined with | |
| the bitwise OR operator ("|"). For example, a token that appears in | |
| the dictionary as both a noun and an adjective will have a token type | |
| value of (PRSTYP_ADJ | PRSTYP_NOUN). | |
| Because more than one PRSTYP_xxx value can be combined into a type | |
| code, you must use the bitwise AND operator ("&") to check a type code | |
| for a specific PRSTYP_xxx value. For example, if you want to check a | |
| token to see if has "noun" among its types, you'd write this: | |
| ((typeList[3] & PRSTYP_NOUN) != 0) | |
| - A new built-in function lets you perform a parser dictionary look-up | |
| to obtain the list of objects that define a set of vocabulary words. | |
| This function can be used to perform your own noun-phrase parsing. | |
| Call the new function like this: | |
| objList := parserDictLookup(tokenList, typeList); | |
| The 'tokenList' parameter is a list of the token strings you want to | |
| look up in the dictionary; this list uses the same format as the list | |
| returned by parserTokenize(), so you can use the result of | |
| parserTokenize() as input to parserDictLookup(). | |
| The 'typeList' parameter is a list of token types. Each entry in | |
| 'typeList' gives the token type of the corresponding entry in | |
| 'tokenList'. This list uses the same PRSTYP_xxx codes returned by | |
| parserGetTokTypes(), but each entry in the type list should have only | |
| a single PRSTYP_xxx code (a type code in this list should not be a | |
| combination of more than one PRSTYP_xxx code). | |
| Because the 'typeList' entries must contain individual PRSTYP_xxx type | |
| codes, rather than combinations of type codes, you should generally | |
| not pass the result of parserGetTokTypes() directly to to | |
| parserDictLookup(). Instead, you need to determine how you want to | |
| interpret the words in the token list by choosing a single token type | |
| for each entry. How you determine each single type is up to you. If | |
| you're parsing a noun phrase, for example, you might decide that all | |
| words in the noun phrase except the last must be adjectives, and the | |
| last must be a noun. The assignment of token types will depend on the | |
| type of parsing you're doing, and the syntax rules that you decide to | |
| implement for the type of input you're parsing. | |
| The return value of parserDictLookup() is a list of all of the game | |
| objects that match all of the vocabulary words, with the given types. | |
| If there are no objects that match all of the words, the result is an | |
| empty list. | |
| Verbs that use combining prepositions (such as "pick up" or "go | |
| north") use a special form of the token string. To look up a | |
| combining, two-word verb, use a token string that contains both words, | |
| separated by a space. parserTokenize() will never return such a | |
| string, because it will always break up the tokens according to word | |
| separators, so you must re-combine such tokens yourself. For example, | |
| to look up the deepverb object matching "pick up" as a verb, you could | |
| write this: | |
| objList := parserDictLookup(['pick up'], [PRSTYP_VERB]); | |
| Note that parserDictLookup() simply looks up words in the dictionary. | |
| This function doesn't perform any disambiguation, access checking, | |
| visibility checking, or any other validation on the objects. | |
| - A new built-in function allows the game to invoke the parser's | |
| internal function to parse a noun list. This can be used in | |
| conjunction with the parseUnknownVerb to allow the function to | |
| interpret part of the word list as a noun list. | |
| Your game program can call the function like this: | |
| ret := parseNounList(wordlist, typelist, startingIndex, | |
| complainOnNoMatch, multi, checkActor); | |
| 'wordlist' is a list of the strings making up the command. 'typelist' | |
| is a list of word types; each entry in 'typelist' is a number giving | |
| the type of the corresponding word in 'wordlist'. The values in | |
| 'wordlist' have the same meaning as the 'typelist' parameter to the | |
| parseUnknownVerb function: | |
| PRSTYP_ARTICLE - the word is defined as an article | |
| PRSTYP_ADJ - adjective | |
| PRSTYP_NOUN - noun | |
| PRSTYP_PLURAL - plural | |
| PRSTYP_PREP - preposition | |
| PRSTYP_VERB - verb | |
| PRSTYP_SPEC - special word (".", "of", "and", etc.) | |
| PRSTYP_UNKNOWN - the word is not in the dictionary | |
| 'startingIndex' is the index in 'wordlist' and 'typelist' of the first | |
| word to parse; the function will ignore all of the words before this | |
| index. This allows you to parse a portion of a word list in your own | |
| code, and start parsing a noun phrase that follows the portion you | |
| parsed. | |
| Set 'complainOnNoMatch' to true to make the function display an error | |
| message if it parses a syntactically valid noun phrase, but there are | |
| no objects in the game that match the noun phrase; set this to nil if | |
| you want to suppress this message. Note that the function will | |
| display any syntax error messages regardless of this setting. If you | |
| want to suppress all messages, you can use outhide() or outcapture() | |
| to hide any error messages displayed. | |
| 'multi' specifies whether you want the function to parse multiple noun | |
| phrases (separated by "and", for example) or just a single noun | |
| phrase. If 'multi' is true, the function will parse any number of | |
| noun phrases; if 'multi' is nil, the function will only parse a single | |
| phrase, stopping if it reaches "and" or equivalent. | |
| 'checkActor' specifies if you want to perform an actor check. If this | |
| is true, the function will reject "all", quoted strings, and phrases | |
| involving "both" or "any"; it will only parse a single noun phrase | |
| (regardless of the setting of 'multi'); and it will not display an | |
| error if the noun phrase cannot be matched. The parser uses this mode | |
| internally to check the beginning of a command to determine if the | |
| command is directed to an actor, and this is probably the only context | |
| in which 'checkActor' should ever need to be true. In most cases, you | |
| should set 'checkActor' to nil. Note that you should not use true | |
| just because noun phrase may happen to contain an actor or is expected | |
| to contain an actor; you should only use true when you want the | |
| special error-handling behavior. Note also that using true for | |
| 'checkActor' does not cause the parser to reject noun phrases that | |
| refer to non-actor objects; this flag simply controls the | |
| error-handling behavior and does not affect what objects can be | |
| matched. | |
| If the parser encounters a syntax error, the function returns nil. | |
| This indicates that the function displayed an error message | |
| (regardless of the value of 'complainOnNoMatch'), and that the words | |
| do not form a syntactically-correct noun phrase. | |
| If the parser finds a syntactically valid noun phrase, but finds no | |
| objects that match the noun phrase, it returns a list containing a | |
| single number. The number is the index of the next word in 'wordlist' | |
| following the noun phrase. For example, suppose we have this word | |
| list: | |
| ['take' 'red' 'ball' 'with' 'hook'] | |
| Suppose that we start parsing at index 2 ('red'), and that 'red' and | |
| 'ball' are in the dictionary as adjective and noun, respectively. | |
| The parser will parse the noun phrase "red ball", consuming two | |
| words from the word list. Now, suppose that there are no objects | |
| in the game matching both vocabulary words (i.e., there's no red | |
| ball in the game). The parser will indicate that a syntactically | |
| valid noun phrase is present, but that no objects match the noun | |
| phrase, by returning this: | |
| [4] | |
| The number is the index of the next word after the noun phrase (in | |
| this example, 'with'). | |
| If the parser finds a syntactically valid noun phrase, and finds one | |
| or more matching objects, it returns a list giving the matching | |
| objects. The first element of the list, as above, is the index in the | |
| word array of the next word after the noun phrase. Each additional | |
| element is a sublist. | |
| Each sublist gives information on one noun phrase. If 'multi' is nil, | |
| there can be at most one sublist. If 'multi' is true, there will be | |
| one sublist per noun phrase (each noun phrase is separated from the | |
| previous one by "and" or equivalent). The first element of the | |
| sublist is the index in the word array of the first word of the noun | |
| phrase, and the second element is the index of the last word of the | |
| noun phrase; the noun phrase is formed by the words in the array from | |
| the first index to the last index, inclusive, so the last index will | |
| always be greater than or equal to the first index. After these two | |
| elements, the sublist contains pairs of entries: a matching object, | |
| and flags associated with the matching object. Each matching object | |
| is a game object that matches all of the vocabulary words in the noun | |
| phrase. | |
| The flags value associated with each matching object is a combination | |
| of any number of the PRSFLG_xxx values described with the | |
| parseNounPhrase function. These flag values can be combined with the | |
| bitwise OR operator ("|"), so to test for a particular flag value, use | |
| the bitwise AND operator: ((flag & PRSFLG_EXCEPT) != 0). | |
| Since the return list is rather complicated, some examples might be | |
| helpful. | |
| Suppose that we start with this word list: | |
| ['take' 'knife' ',' 'cardboard' 'box'] | |
| Suppose also that we use 2 as the starting index (because we want to | |
| start at the word 'knife'), and that 'knife', 'cardboard' and 'box' | |
| are defined words in the game. | |
| Now, suppose we have the following game objects defined: | |
| rustyKnife: item noun='knife' adjective='rusty'; | |
| sharpKnife: item noun='knife' adjective='sharp'; | |
| dagger: item noun='dagger' 'knife'; | |
| box: item noun='box' adjective='cardboard'; | |
| Given all of this, the return list would look like this: | |
| [6 [2 2 rustyKnife 0 sharpKnife 0] [4 5 box 0]] | |
| The first element indicates that the next word after the noun list is | |
| element 6; since the list has only five elements, this simply means | |
| that the noun list runs all the way to the end of the word list. | |
| The next two elements are the sublists, one per noun phrase: | |
| [2 2 rustyKnife 0 sharpKnife 0] | |
| [4 5 box 0] | |
| The first sublist specifies a noun phrase that runs from word 2 to | |
| word 2, inclusive, hence 'knife'. The remaining pairs of elements in | |
| the list tell us that the matching objects are rustyKnife (with flags | |
| of 0) and sharpKnife (also with flags of 0). | |
| The second sublist specifies a noun phrase that runs from word 4 to | |
| word 5, inclusive, hence 'cardboard box'. The matching object for | |
| this phrase is box (with flags 0). | |
| To interpret this return value, consider this code: | |
| if (ret = nil) | |
| { | |
| /* the noun phrase had a syntax error; give up */ | |
| return; // or whatever we want to do in case of error | |
| } | |
| "Next word index = <<ret[1]>>\b"; | |
| if (length(ret) = 1) | |
| { | |
| /* valid noun phrase, but no matching objects */ | |
| "I don't see that here."; | |
| return; | |
| } | |
| /* handle each sublist individually */ | |
| for (i := 2 ; i <= length(ret) ; ++i) | |
| { | |
| local sub; | |
| local firstWord, lastWord; | |
| local j; | |
| /* get the current sublist */ | |
| sub := ret[i]; | |
| /* get the first and last word indices for this noun phrase */ | |
| firstWord := sub[1]; | |
| lastWord := sub[2]; | |
| /* display the word list (or whatever - this is just an example) */ | |
| "\bNoun phrase #<<i>> is: '"; | |
| for (j := firstWord ; j <= lastWord ; ++j) | |
| { | |
| say(wordlist[j]); | |
| if (j != lastWord) | |
| say(' '); | |
| } | |
| "'\n"; | |
| /* scan the objects in the list - each object takes two elements */ | |
| for (j := 3 ; j <= length(sub) ; j += 2) | |
| { | |
| /* display this object and its flags */ | |
| "matching object = <<sub[j].sdesc>>, flags = <<sub[j+1]>>\n"; | |
| } | |
| } | |
| Note that in many cases you won't care about interpreting this list | |
| directly; instead, you'll simply want to pass the list to the | |
| parserDisambig() built-in function for resolution and disambiguation. | |
| The return list is in the same format required for input to that | |
| function. | |
| This function directly invokes the parser's noun list parser, which is | |
| exactly the same code the parser uses while parsing a player's command | |
| line. The noun list parser will in turn invoke your parseNounPhrase() | |
| function, if you've defined such a function in your game. So, you | |
| should be careful not to set up an infinite recursion by calling this | |
| function from your parseNounPhrase() function. | |
| - A new built-in function lets you access the parser's object resolution | |
| and disambiguation subsystem programmatically. You can use the object | |
| resolver in conjunction with parseNounList() or with your own | |
| noun-list parser to implement your own command parsing system. | |
| In the standard TADS parser, object resolution occurs after the parser | |
| has finished parsing the syntax structure of a sentence, and thus | |
| knows the verb, all of the noun phrases, and connecting prepositions. | |
| Once all of this information is known, the parser can intelligently | |
| determine the objects to which each noun phrase refers. As a result | |
| of this design, the object resolver requires parameters that specify | |
| the other aspects of the sentence structure. | |
| The object resolution function is called like this: | |
| resultList := parserResolveObjects(actor, verb, prep, otherobj, | |
| usageType, verprop, | |
| tokenList, objList, silent); | |
| The 'actor' parameter is the actor object for the command for which | |
| the objects are to be resolved. The 'verb' parameter is the deepverb | |
| object involved in the command. The 'prep' parameter is the | |
| preposition object that introduces the indirect object; if there's no | |
| indirect object or no preposition, 'prep' should be nil. | |
| 'usageType' specifies the type of object that you're resolving. You | |
| should use one of these constants, defined in adv.t, for this | |
| parameter: | |
| PRO_RESOLVE_DOBJ - direct object | |
| PRO_RESOLVE_IOBJ - indirect object | |
| PRO_RESOLVE_ACTOR - actor: use this if you're resolving an object | |
| for use as an actor to whom the player is directing the command. | |
| 'verprop' is the verification method address; this is the address of | |
| the verDoVerb for your verb. This must be specified in addition to | |
| the deepverb object, because a single deepverb can be associated with | |
| multiple verification/action methods (for example, "put x on y" uses a | |
| different set of methods from "put x in y", but both are associated | |
| with putVerb). For example, for the direct object of "put x in y", | |
| you'd specify &verDoPutIn. | |
| If you're validating an actor (not a direct or indirect object that | |
| happens to be an actor, but rather an actor that the player is | |
| addressing and who is to carry out a command), the parser normally | |
| uses &verDoTake for 'verprop', rather than the actual verb being | |
| executed, because the point is to verify that the player can access | |
| the actor, not that the player can perform the command on the actor. | |
| "Taking" the actor has reasonably suitable accessibility rules for | |
| talking to an actor. You could conceivably define your own verb | |
| simply for the purposes of talking to an actor, and then use that verb | |
| and its appropriate verification method instead of takeVerb and | |
| verDoTake. | |
| 'tokenList' is the list of tokens which was parsed to build the input | |
| object list. If you obtained the object list from parseNounList(), | |
| you should simply use the same token list that you used with | |
| parseNounList(). The importance of 'tokenList' is that the token list | |
| indices in the object list refer to words in the token list. | |
| 'objList' is the input object list. The resolver starts with this | |
| list to produce the resolved list. 'objList' is in exactly the same | |
| format as the list returned by parseNounList(), so you can use the | |
| result of parseNounList() as the 'objList' parameter. If you use your | |
| own noun list parser instead, you must prepare a list that uses the | |
| same format as the parseNounList() result. | |
| 'silent' specifies whether the resolver is interactive or not. If | |
| 'silent' is true, the resolver will not display any messages to the | |
| player, and will not ask the player to resolve the list in case of | |
| ambiguity; instead, the resolver will simply return an error code. If | |
| 'silent' is nil, the resolver will display a message if an error | |
| occurs, and will ask the user to resolve ambiguity using the | |
| traditional interactive process ("Which foo do you mean..."). | |
| The return value of this function is always a list. The first element | |
| of this list is always a number giving a status code. The status | |
| codes are the same values and have the same meanings as the codes | |
| passed to parseError() and parseErrorParam(). | |
| The status code PRS_SUCCESS (this constant and the PRSERR_xxx | |
| constants mentioned below are defined in adv.t) indicates that the | |
| resolution was successful. In this case, the remainder of the list | |
| simply contains the resolved objects: | |
| [PRS_SUCCESS goldCoin shoeBox] | |
| PRSERR_AMBIGUOUS indicates that the result list is ambiguous. This | |
| code will only be returned if 'silent' is true, because in other cases | |
| the resolver will not return until the player resolves any ambiguity | |
| interactively, or an error occurs. When this status code is returned, | |
| the remainder of the list contains the partially-resolved objects; the | |
| resolver will have narrowed down the list as much as possible by | |
| including only objects that are accessible to the actor for the | |
| purposes of the verb, but the list will still require further | |
| disambiguation to obtain the final set of objects. | |
| [PRSERR_AMBIGUOUS goldCoin silverCoin shoeBox cardboardBox] | |
| PRSERR_DISAMBIG_RETRY indicates that the player entered a new command | |
| in response to a disambiguation query. This can only happen when | |
| 'silent' is nil, because the parser won't ask the player any questions | |
| at all when 'silent' is true. When this status code is returned, the | |
| list contains only one additional element, which is a string with the | |
| player's new command. If you want to execute the new command, you can | |
| use parserReplaceCommand() to abandon the current command and execute | |
| the new command instead. | |
| [PRSERR_DISAMBIG_RETRY 'go north'] | |
| Any other status code indicates an error which caused the resolver to | |
| fail. The list will contain no other elements in these cases. | |
| Note that this function calls the identical internal parser code that | |
| the player command parser normally uses to process a command. The | |
| object resolver in some cases calls the disambigDobj and disambigIobj | |
| methods defined in the deepverb object. As a result, you should be | |
| careful not to call this function from disambigDobj or disambigIobj | |
| methods, since doing so could result in infinite recursion. | |
| Here's an example that uses several of the new parser functions, | |
| including parserResolveObjects(). This function reads a string from | |
| the keyboard, tokenizes it, gets the token types, parses the token | |
| list as a noun list, and then resolves the noun list to an object. | |
| askForObject: function | |
| { | |
| local str; | |
| local toklist, typelist; | |
| local objlist; | |
| /* get an object */ | |
| "Type an object name: "; | |
| str := input(); | |
| /* tokenize it */ | |
| toklist := parserTokenize(str); | |
| if (toklist = nil) | |
| { | |
| "The object name is invalid!"; | |
| return nil; | |
| } | |
| /* get the token types */ | |
| typelist := parserGetTokTypes(toklist); | |
| /* parse a single noun phrase */ | |
| objlist := parseNounList(toklist, typelist, 1, true, nil, nil); | |
| if (objlist = nil) | |
| return nil; | |
| if (length(objlist) = 1) | |
| { | |
| "You see no such thing. "; | |
| return nil; | |
| } | |
| if (objlist[1] <= length(toklist)) | |
| { | |
| "There seem to be words after the object name that I can't use. "; | |
| return nil; | |
| } | |
| /* resolve and disambiguate */ | |
| objlist := parserResolveObjects(Me, takeVerb, nil, nil, | |
| PRO_RESOLVE_DOBJ, &verDoTake, | |
| toklist, objlist, nil); | |
| if (objlist[1] = PRS_SUCCESS) | |
| { | |
| /* success! return the objects, which follow the status code */ | |
| return cdr(objlist); | |
| } | |
| else if (objlist[1] = PRSERR_DISAMBIG_RETRY) | |
| { | |
| /* run the new command, which is in the second element */ | |
| parserReplaceCommand(objlist[2]); | |
| } | |
| else | |
| { | |
| /* we were in non-silent mode, so the resolver displayed an error */ | |
| return nil; | |
| } | |
| } | |
| - A new built-in function, parserReplaceCommand(), allows you to abort | |
| the current command and start executing a new command using a given | |
| text string. Call the function like this: | |
| parserReplaceCommand(commandString); | |
| This function doesn't return -- it effectively executes an "abort" | |
| statement to terminate the current command. The given command string | |
| is entered into the parser's internal buffer, and the system parses | |
| and executes the command as though the player had typed the command | |
| directly. | |
| - A new built-in function, setOutputFilter, allows the game program to | |
| intercept and optionally change all display output just before it's | |
| formatted for display. setOutputFilter() takes one argument, which | |
| is the address of a user-defined function; this tells the output | |
| formatter to call this function each time a string is displayed. | |
| You can also call the function with a value of nil, which cancels | |
| the current output filter function, restoring unfiltered output. | |
| The filter function is defined like this: | |
| myFilter: function(str) | |
| You can use any name in place of "myFilter". The parameter "str" | |
| is the string that the output formatter is about to display. Your | |
| filter function can return nil, in which case the original string | |
| is displayed unchanged; or it can return a (single-quoted) string | |
| value, which the formatter will display instead of the original | |
| string. | |
| Here's an example that converts all displayed output to upper-case | |
| when the player types the verb "uppercase on," and restored output to | |
| normal when the player types "uppercase off." | |
| ucFilter: function(str) | |
| { | |
| return upper(str); | |
| } | |
| uppercaseonVerb: deepverb | |
| verb = 'uppercase on' | |
| action(actor) = | |
| { | |
| "Upper-case mode is now ON. "; | |
| setOutputFilter(ucFilter); | |
| } | |
| ; | |
| uppercaseoffVerb: deepverb | |
| verb = 'uppercase off' | |
| action(actor) = | |
| { | |
| "Upper-case mode is now OFF. "; | |
| setOutputFilter(nil); | |
| } | |
| ; | |
| The output filter function is called before the output formatter does | |
| any processing on the text to display. After the filter returns, the | |
| formatter translates "%fmt%" sequences, applies caps() and nocaps() | |
| changes, translates "\t" and similar sequences, performs word-wrapping | |
| on the line, adjusts spacing for punctuation, and, when in HTML mode, | |
| interprets HTML mark-ups. As a result, you can perform your own | |
| translations on any of these sequences; in addition, you can use such | |
| sequences in the returned string, and the formatter will interpret them | |
| normally. | |
| Note that the output formatter will make a separate call to your filter | |
| function to display the result of translating each "%fmt%" sequences in | |
| the text to be displayed. These separate calls will occur after your | |
| filter function returns. This allows you to perform the same operations | |
| on the translated "%fmt%" sequences that you perform on any other text. | |
| - Fixed a bug in the objwords() built-in function. In the past, this | |
| function at times incorrectly returned a non-empty list of words for | |
| a command that didn't have the corresponding object at all. This has | |
| been corrected; objwords() will now return an empty list when the | |
| command does not have the requested object. | |
| - The parser now re-validates objects in a multi-object command after | |
| executing the command on the first object. This re-validation occurs | |
| before the verbAction method is called for each object, and is done | |
| only for the second and subsequent object in a multi-object command. | |
| Re-validation is necessary because an action performed on the first | |
| object in a multi-object command could change conditions in the game | |
| such that the second object becomes invalid for the command, even | |
| though it was valid during the object resolution phase. Consider | |
| this example: | |
| >x large box | |
| The large box is open. It contains a small box. | |
| >x small box. | |
| The small box is open. | |
| >close large box, small box | |
| large box: Closed. | |
| small box: You don't see that here. | |
| In the past, the parser only validated the objects in a multi-object | |
| command during the resolution phase, which happens before executing | |
| the command on any of the objects. Since the small box is accessible | |
| before the large box is closed, the parser would have allowed the | |
| player to close the small box after closing the large box in the last | |
| command above. By re-validating the second and subsequent objects, | |
| the parser now correctly detects when objects become inaccessible in | |
| the course of executing a command on multiple objects. | |
| If an object fails re-validation, the parser checks to see if the | |
| object is visible. If it is, the parser uses the cantReach method | |
| as usual to display the error. If the object is not visible, the | |
| parser displays error 38, "You don't see that here any more." | |
| - The parser error codes (the numbers passed to parseError and | |
| parseErrorParam, and returned by parserResolveObjects) now have | |
| constants defined in adv.t. These constants have names like | |
| PRSERR_xxx. Refer to adv.t for the complete list. | |
| You may prefer to use the constants in your code whenever possible for | |
| clarity. The numeric values will be stable in future releases, so | |
| it's perfectly safe to use the numbers directly, but using the | |
| symbolic constants will make the purpose of your code clearer to | |
| others (and to you, if you come back to some code after not having | |
| looked at it for a few weeks). | |
| - Several new parser error codes have been added. | |
| The new codes 40, 41, 42, 43, and 44 never result in a | |
| parser-generated error message, thus these codes have no default | |
| message. These codes can, however, be returned by | |
| parserResolveObjects(), so that callers can distinguish among | |
| different types of error conditions if necessary. | |
| Code 38, default message "You don't see that here any more." This | |
| message is used during object re-validation when an object that was | |
| valid at the start of the command is no longer valid. | |
| Code 39, default message "You don't see that here." This new message | |
| is used when object validation (via validDo or validIo) fails in the | |
| course of executing a command recursively with execCommand(). | |
| Code 40, no default message. This error code is used when the parser | |
| is unable to create a new generic numeric object when calling a | |
| newNumbered method. | |
| Code 41, no default message. This error occurs when disambigDobj or | |
| disambigIobj returns an invalid status code. | |
| Code 42, no default message. This error occurs when the parser gets | |
| an empty line of text in response to a disambiguation question. | |
| Code 43, no default message. This error occurs when the parser gets | |
| what looks like an entirely new command in response to a | |
| disambiguation question. | |
| Code 44, no default message. This occurs when the parser finds that | |
| it still has an ambiguous list of objects after applying validation | |
| and verification tests, but the disambiguator is being called in | |
| "silent" mode and hence is not allowed to ask the player for help. | |
| When this occurs, the parser simply returns this error to indicate | |
| that the list cannot be disambiguated. | |
| - Parser message 16 has been changed slightly. The parser generates | |
| message 16 when the player's answer to a noun disambiguation question | |
| ("which <object> do you mean...") doesn't refer to any of the possible | |
| ambiguous objects. For example, suppose the parser asks "which book | |
| do you mean, the red book, or the blue book?," and the player answers | |
| "silver." In the past, message 16 simply said "I don't see that | |
| here." Some players found this slightly confusing, especially since | |
| the answer may have referred to some other object that wasn't a book | |
| but was indeed present. The new message is intended to be somewhat | |
| more explicit: "You don't see any %s %s here," where the first "%s" | |
| is replaced with the new noun phrase the player typed, and the second | |
| "%s" is replaced with the original noun phrase. | |
| >take book | |
| Which book do you mean, the red book, or the blue book? | |
| >the silver one | |
| You don't see any silver book here. | |
| Note that you can use the parseErrorParam() user-defined function if | |
| you want to intercept this message have have access to the string | |
| parameters. | |
| - In adv.t, the "doorway" class has a new method, setIslocked. | |
| This new method behaves analogously to setIsopen: when you call | |
| setIslocked on a door, the method automatically updates both sides | |
| of a two-sided door to the same locking status. All of the code | |
| in "doorway" that updates the islocked property now uses setIslocked | |
| rather than changing the property directly. | |
| - The default "sav" extension on newly-created saved game files is now | |
| in lower-case letters on operating systems that support mixed-case | |
| filenames. | |
| - A long-standing parser bug involving vocabulary words defined as both | |
| adjectives and nouns (for different objects) has been fixed. In the | |
| past, if a particular word was defined as a noun for one object, and | |
| as an adjective for another object, the word could not ever be used | |
| alone to refer to the second object. For example, suppose the game | |
| defined one object, "letter," and another, "letter opener"; the word | |
| "letter" is defined as a noun for the first object, and as an adjective | |
| for the second object. If the player typed "get letter," the parser | |
| formerly assumed that the word was to be used as a noun; even if the | |
| "letter" object wasn't present and the "letter opener" object was, | |
| the parser would never understand the sentence to mean "get letter | |
| opener." This was inconsistent with other objects, since in other | |
| cases an adjective alone could be used to refer to an object when the | |
| meaning was not ambiguous. This has now been fixed; the parser now | |
| will interpret the adjective alone to refer to an associated object | |
| when no other object defining the word as a noun is accessible. | |
| Note that this does not create a new ambiguity: if the player types | |
| "get letter," and both the letter and the letter opener objects | |
| are present, the parser will still assume that "letter" is being | |
| used as a noun. The parser will only attempt the adjective-only | |
| interpretation as a last resort, when no matching objects using the | |
| word as a noun are accessible. | |
| - Fixed a parser bug involving disambiguation of objects with redundant | |
| nouns or adjectives. When the player types a word, and the word is at | |
| least six characters long, the parser will match the word to any entry | |
| in the game's dictionary of which the player's word is a leading | |
| substring. For example, "flashlig" matches "flashlight", because | |
| "flashlig" is eight characters long (which is greater than or equal | |
| to the required six), and matches the first eight characters of | |
| "flashlight". Similarly, "headlight" will match "headlights", | |
| because "headlight" is nine characters long, and matches "headlights" | |
| in the first nine characters. This short-hand feature is meant as a | |
| convenience for the player, but occasionally caused ambiguity problems | |
| in past versions of TADS. In particular, when a single object had | |
| a vocabulary word that was a leading substring of another vocabulary | |
| word of the same object (for example, if an object defined as nouns | |
| both 'headlight' and 'headlights'), the object could in some cases | |
| show up twice in a list of ambiguous objects ("which headlight do | |
| you mean..."). The parser now always eliminates redundant entries | |
| in lists of ambiguous objects during object disambiguation. | |
| - Another parser bug involving truncated words has been fixed. Suppose | |
| the game defines one object with a noun 'prince', and another object | |
| with a noun 'princess'. In the past, these two objects both matched | |
| the word 'prince' in a player's command, so if both objects were | |
| present, the parser considered them ambiguous. The parser now | |
| considers an exact match to be better than a truncated match, so the | |
| word 'prince' in a player's command will now match the 'prince' object | |
| more strongly than it will the 'princess' object. If only the | |
| 'princess' object is accessible, 'prince' will still match the | |
| 'princess' object, but if both are present, the parser will simply | |
| assume the player is referring to the 'prince' object. | |
| - Fixed a parser bug that caused two error messages (without any spacing | |
| between them) when the first word in a command was unknown: the | |
| parser responded with both "There's no verb in that sentence!" and | |
| "I don't know the word 'xxx'". The parser now displays only the | |
| first ("no verb") message. | |
| - Fixed a parser bug that caused fuses and deamons to be skipped when | |
| an "exit" statement was executed in the midst of executing a command | |
| involving multiple direct objects. If the "exit" was executed during | |
| the processing of any but the last object in the direct object list, | |
| the parser incorrectly skipped fuses and daemons. This has been | |
| corrected; fuses and daemons will now run after an "exit" is executed, | |
| regardless of how many direct objects are involved in the command. | |
| - In adv.t, in the definition of movableActor, the definitions of the | |
| format strings fmtYou, fmtYour, fmtYoure, fmtYoum, and fmtYouve now | |
| vary according to the gender defined for the object with the isHim | |
| and isHer properties. If isHim evaluates to true, the format strings | |
| now use masculine pronouns; otherwise, if isHer evaluates to true, | |
| the format strings use feminine pronouns; otherwise, they use neuter | |
| pronouns. | |
| Note that, in the past, these format strings always used masculine | |
| pronouns; if you defined any Actor or movableActor objects with | |
| neither isHim nor isHer set to true, messages that in the past used | |
| masculine pronouns will now use neuter pronouns. You should be | |
| careful to ensure that you're defining the appropriate gender property | |
| (isHim = true or isHer = true) for your Actor and movableActor | |
| objects, so that messages using the format strings display the correct | |
| pronouns. | |
| - The text of system error messages 1021 ("index value too low") and | |
| 1022 ("index value too high") have been modified slightly so that | |
| they don't use ">" or "<" characters. (When running in HTML mode, | |
| the "<" and ">" characters in these messages caused problems, because | |
| the renderer tried to parse them as HTML tags.) | |
| - In the past, when the player's command contained a quoted string, | |
| the value that the parser passed to preparseCmd() did not contain | |
| a usable form of the string. This has been corrected; the value | |
| that the parser passes to preparseCmd() will now always be the | |
| original string text, enclosed in double quote marks. You can | |
| therefore detect a string token in the preparseCmd() list by | |
| checking to see if the first character (obtained with substr()) | |
| is a double quote mark, '"': | |
| if (substr(lst[i], 1, 1) = '"') | |
| /* this token is a quoted string */ ; | |
| - In adv.t, the implementation of clothingItem has been modified | |
| slightly, to correct some bugs and make some improvements. | |
| - An actor can now attempt to wear objects that aren't being | |
| carried; the game will now automatically attempt to take the item | |
| (via a recursive "take" command using execCommand) before wearing | |
| it if the actor isn't carrying it. | |
| - The game will also automatically take an object if it's within | |
| another object in the actor's inventory; this ensures that an | |
| object is always at the top level of an actor's inventory while | |
| being worn. | |
| - When doffing a clothingItem, the game first checks to ensure that | |
| the actor's maximum "bulk" carrying capacity is not exceeded; this | |
| check must be made because an item being worn doesn't encumber an | |
| actor with bulk since it's not being carried by hand. If the | |
| maximum bulk would be exceeded, the game will not let the actor | |
| doff the item. | |
| - The verDoWear method now checks to make sure that another actor | |
| isn't wearing the item; in the past, if the player attempted to | |
| wear an item already being worn by another actor, the game replied | |
| with the nonsensical message "you're already wearing that." | |
| ------------------------------------------------------------------------------ | |
| 2.3.0 02/01/1999 | |
| - A new systemInfo() feature code lets the game determine whether | |
| the interpreter is in HTML mode or plain text mode. The new code | |
| is __SYSINFO_HTML_MODE; systemInfo(__SYSINFO_HTML_MODE) returns | |
| true if the interpreter is currently interpreting HTML markups, | |
| nil if not. Note that this new code has nothing to do with whether | |
| the interpreter is a full multimedia system (such as HTML TADS) or | |
| a text-only system (such as the DOS "TR" interpreters); this new | |
| code instead indicates only whether or not a "\H+" sequence is | |
| currently in effect. | |
| - Several new systemInfo() feature codes have been added to provide | |
| information on MPEG audio support: | |
| __SYSINFO_MPEG_AUDIO - this returns 1 if MPEG 2.0 audio support | |
| of any kind is present, 0 if not. It is possible that some | |
| systems may support some types of MPEG audio, but not all | |
| three layers. This feature code indicates whether MPEG | |
| audio of any kind is supported; the specific layer codes | |
| below can be used to check for each individual layer. | |
| __SYSINFO_MPEG_AUDIO_1 - 1 if MPEG 2.0 layer I is supported | |
| __SYSINFO_MPEG_AUDIO_2 - 1 if MPEG 2.0 layer II is supported | |
| __SYSINFO_MPEG_AUDIO_3 - 1 if MPEG 2.0 layer III is supported | |
| - To accomodate the new TADS-Input font feature in HTML TADS, std.t | |
| now has a definition of the commandPrompt and commandAfterRead | |
| functions that automatically switch to the TADS-Input font. This | |
| is important only for HTML-enabled games. If your game uses HTML | |
| features, you should #define USE_HTML_PROMPT before including std.t | |
| in order to use these new functions. (If you're providing your own | |
| definitions of these functions, you should consider adding the font | |
| settings made in the new std.t versions.) | |
| - New feature: TADS now has a built-in regular expression matching | |
| facility. Regular expressions provide a powerful and simple way | |
| to search for a complex pattern within a text string. This feature | |
| is particularly useful for writing preparse() and preparseCmd() | |
| functions, since it allows you to perform complex pattern matching | |
| and replacing with very little code. | |
| The new function reSearch() searches for the first occurrence of a | |
| regular expression pattern within a string. It returns nil if the | |
| pattern is not found. If the pattern is found, the function returns | |
| a list, the first element of which is a number giving the character | |
| position within the string of the start of the match (the first | |
| character is at position 1), the second element giving the number | |
| of characters in the match, and the third element a string giving | |
| the actual text of the match. | |
| ret := reSearch(pattern, string_to_search); | |
| The pattern is specified using regular expression syntax similar to | |
| that used by "grep" and other similar utilities. Here are the basic | |
| building blocks of the regular expression syntax: | |
| | Alternation: matches the expression on the left or the | |
| expression on the right. This operator affects as many | |
| characters as it can, out to the nearest parentheses. | |
| ( ) Groups an expression. | |
| + Indicates that the immediately preceding character or | |
| parenthesized expression repeats one or more times. | |
| * Indicates that the immediately preceding character or | |
| parenthesized expression repeats zero or more times. | |
| ? Indicates that the immediately preceding character or | |
| parenthesized expression can occur zero or one time. | |
| . (a period) Wildcard: matches any single character. | |
| ^ Matches the beginning of the string. | |
| $ Matches the end of the string. | |
| % Quotes the following character, removing the special | |
| meaning of these characters: | . ( ) * ? + ^ $ % [ | |
| Also introduces the special sequences listed later. | |
| [ ] Indicates a character list or range expression. Matches | |
| any one of the listed characters. A range can be specified | |
| by following a character with '-' and another character; | |
| this matches all of the characters between and including | |
| these two characters. For example, [a-z] matches any | |
| one lower-case letter, and [0-9] matches any one digit. | |
| Ranges and single characters can be combined; for example, | |
| [a-zA-Z] matches any letter, upper- or lower-case. To | |
| include the character ']' in a list, make it the first | |
| character after the opening bracket; to include '-', make | |
| it the next character after that. For example, []] matches | |
| just ']', [-] matches just '-', and []-] matches '-' and ']'. | |
| [^ ] Exclusionary character list or range. This matches any | |
| character *except* the ones listed. For example, [^0-9] | |
| matches anything single character except a digit. | |
| %1 This matches the same text that matched the first parenthesized | |
| expression. For example, consider the pattern '(a*).*%1'. | |
| The string 'aaabbbaaa' will match, because the first three | |
| characters match the parenthesized 'a*' expression, which | |
| causes '%1' to match the last three characters; the middle | |
| three characters are matched by the '.*' expression. | |
| %2 Matches the text matching the second parenthesized expression. | |
| And so on through... | |
| %9 Matches the text matching the ninth parenthesized expression. | |
| %< Matches at the beginning of a word. Words are considered to | |
| be contiguous groups of letters and numbers. | |
| %> Matches at the end of a word. For example, '%<and%>' matches | |
| the "and" in 'ball and box' and 'and then', but not in | |
| 'rubber band' or 'the android'. Note that %< and %> do not | |
| actually contribute any characters to the match - they simply | |
| ensure that they fall on a word boundary. So, searching for | |
| '%<and%>' in 'ball and box' matches the string 'and' -- the | |
| spaces are not included in the match. | |
| %w Matches any word character (a letter or a digit). | |
| %W Matches any non-word character (anything but a letter or digit). | |
| %b Matches at any word boundary (beginning or end of a word). | |
| %B Matches except at a word boundary. | |
| Any character other than those listed above simply matches the exact | |
| same character. For example, 'a' matches 'a'. | |
| Here are some examples of simple regular expressions, to help clarify | |
| the meanings of the basic building blocks: | |
| abc|def either 'abc' or 'def' | |
| (abc) 'abc' | |
| abc+ 'abc', 'abcc', 'abccc', etc. | |
| abc* 'ab', 'abc', 'abcc', 'abccc', etc. | |
| abc? 'ab' or 'abc' | |
| . any single character | |
| ^abc 'abc', but only at the start of the string | |
| abc$ 'abc', but only at the end of the string | |
| %^abc literally '^abc' | |
| [abcx-z] 'a', 'b', 'c', 'x', 'y', or 'z' | |
| []-] ']' or '-' | |
| [^abcx-z] any character except 'a', 'b', 'c', 'x', 'y', or 'z' | |
| [^]-q] any character except ']', '-', or 'q' | |
| Here are some more complicated examples: | |
| (%([0-9][0-9][0-9]%) *)?[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9] | |
| This matches a North American-style telephone number, either with | |
| or without an area code in parentheses. If an area code is | |
| present, it can optionally be separated by spaces from the rest | |
| of the number: '(415)555-1212', '555-1212', '(415) 555-1212'. | |
| [-+]?([0-9]+%.?|([0-9]*)%.[0-9]+)([eE][-+]?[0-9]+)? | |
| This matches a floating-point number in the notation used by C and | |
| some other programming languages: either a string of digits optionally | |
| ending with a decimal point, or zero or more digits followed by a | |
| decimal point followed by one or more digits; optionally followed | |
| by an exponent specified with the letter "E" (upper- or lower-case), | |
| an optional sign ('+' or '-'), and one or more digits; all of this | |
| can be preceded by an optional sign. This matches: '3e9', '.5e+10', | |
| '+100', '-100.', '100.0', '-5e-9', '-23.e+50'. | |
| ^ *tell%>(.*)%<to%>(.*) | |
| This matches the word "tell" at the beginning of the string, | |
| preceded only by zero or more spaces, followed by any text, followed | |
| by the word "to", followed by any more text. This matches | |
| 'tell bob to go north' and 'tell teeterwaller to give me the mask'. | |
| Here's a code example: | |
| ret := reSearch('d.*h', 'abcdefghi'); | |
| if (ret = nil) | |
| "No match."; | |
| else | |
| "Start = <<ret[1]>>, length = <<ret[2]>>, text = \"<<ret[3]>>\". "; | |
| When run, this code will display the following: | |
| Start = 4, length = 5, text = "defgh". | |
| - Another new built-in function, reGetGroup(), lets you retrieve the | |
| matching text for parenthesized groups within regular expressions. | |
| reGetGroup() returns information about the last call to reSearch(). | |
| The function takes one argument, which is a number giving the group | |
| number to retrieve: the first parenthesized expression is group 1, | |
| the second is group 2, and so on. (When groups are nested, the | |
| position of the open parenthesis is used to determine the group | |
| numbering. The leftmost open parenthesis is numbered as group 1.) | |
| reGetGroup() returns nil if there is no such group in the most | |
| recent regular expression or if the last call to reSearch() did not | |
| match the expression. Otherwise, reGetGroup() returns a list with | |
| three elements identifying the group. The first element is a | |
| number giving the character position within the original search | |
| string of the start of the text that matched the group. The second | |
| element is the length of the text that matched the group. The third | |
| element is the actual text that matched the group. | |
| Here's a code example: | |
| ret := reSearch('d(.*)h', 'abcdefghi'); | |
| if (ret != nil) | |
| { | |
| grp := reGetGroup(1); | |
| if (grp != nil) | |
| "Start = <<grp[1]>>, len = <<grp[2]>>, text = \"<<grp[3]>>\". "; | |
| } | |
| This will display the following: | |
| Start = 5, len = 3, text = "efg". | |
| You can use regular expression grouping to carry out complicated | |
| transformations on strings with relatively little code. Since you | |
| determine exactly where in a string a particular group in a regular | |
| expression occurs, you can take the group text out of the string | |
| and put it back into the string in a different order or with other | |
| changes. | |
| For example, suppose you want to write a preparse() function that | |
| finds sentences of the form "tell <actor> to <command>" and converts | |
| them to the normal TADS actor command format, "<actor>, <command>". | |
| You can use regular expression grouping to find this pattern of text | |
| and build the new command from pieces of the original: | |
| ret := reSearch('^ *tell%> *(.*)%<to%> *(.*)', cmd); | |
| if (ret != nil) | |
| cmd := reGetGroup(1)[3] + ', ' + reGetGroup(2)[3]; | |
| Or, suppose you have a telephone in your game, and you want to let | |
| the player dial numbers on the phone using normal North American | |
| telephone number notation, including an area code. The TADS parser | |
| won't normally let you do this, since it would try to parse the | |
| number as several words. You could solve this problem using preparse: | |
| after the player enters a command, find anything that looks like a | |
| telephone number, and enclose it in quotation marks; this will make | |
| the parser treat the phone number as a quoted string, so you can | |
| write your "dial" verb so that it uses strObj as the direct object. | |
| Here's how you could write the preparse routine: | |
| ret := reSearch('(%([0-9][0-9][0-9]%) *)?' | |
| + '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]', cmd); | |
| if (ret != nil) | |
| cmd := substr(cmd, 1, ret[1] - 1) + ' "' + ret[3] + '" ' | |
| + substr(cmd, ret[1] + ret[2], length(cmd)); | |
| - New feature: a new function extends the capabilities of the existing | |
| input functions, input() and inputkey(), to give you more control | |
| over event processing. The new function, inputevent(), can read | |
| multiple types of events, and can also apply a timeout to limit the | |
| how long it waits for an event to occur. | |
| The inputevent() function takes zero or one argument. With no | |
| arguments, inputevent() simply waits until an event occurs. With | |
| one argument, which must be a number, inputevent() waits until an | |
| event occurs, or until the number of milliseconds specified by the | |
| argument has elapsed without an event occurring, in which case the | |
| function "times out" and returns without any event having occurred. | |
| Note that the timeout value, if given, may not always be obeyed | |
| to the exact millisecond. Different types of computers have | |
| different system clock resolutions; in addition, multi-user and | |
| multi-tasking systems often have unpredictable latencies for event | |
| processing. As a result, if you specify a timeout value, the actual | |
| time that elapses before the function times out and returns may be | |
| slightly longer than the specified timeout value. Any additional | |
| latency should be no more than a few hundred milliseconds in most | |
| cases, so this shouldn't be noticeable for most purposes. | |
| The function returns a list value describing the event that occurred. | |
| The first element of the list is a number that specifies the type of | |
| the event. The rest of the list varies according to the event type. | |
| Constants for the event codes are defined in adv.t. The possible | |
| event codes are: | |
| INPUT_EVENT_KEY - the user pressed a key. The second element of | |
| the list returned by inputevent() in this case is a string containing | |
| the key that the user pressed. The string is the same that would be | |
| returned by inputkey() for the same keystroke. | |
| INPUT_EVENT_HREF - the user clicked on an <A HREF=xxx> link. This | |
| event is only returned by an HTML TADS interpreter, never by a | |
| character-mode TADS interpreter. The second element of the return | |
| list is a string containing the text of the HREF that the user | |
| clicked. | |
| INPUT_EVENT_TIMEOUT - no event occurred before the specified timeout | |
| elapsed. The return list contains no additional elements. | |
| INPUT_EVENT_EOF - this indicates that the TADS interpreter is | |
| terminating or an error occurred reading an event. | |
| INPUT_EVENT_NOTIMEOUT - this is not actually an event, but an error | |
| indicating that the current system does not support the timeout | |
| feature of inputevent(). If this occurs, you can still use | |
| inputevent(), but you cannot specify a timeout. The DOS TADS | |
| interpreters (TR, TRX, TR32) all support timeouts, as does HTML TADS | |
| for Windows; interpreters on most systems should be able to support | |
| this feature, but a few systems may not be able to. | |
| - New feature: the inputkey() function now returns a portable | |
| representation for certain extended keys. In the past, keys outside | |
| of the normal ASCII character set were almost impossible to use with | |
| inputkey(), because TADS returned a useless "raw" format for extended | |
| keys, such as cursor navigation keys and function keys. TADS now | |
| uses a portable string format to represent many common keys. | |
| Each extended key is represented as a string containing a key name | |
| enclosed in square brackets. The key name are: | |
| [bksp] - backspace (destructive backspace/delete left) | |
| [up] - up arrow (cursor navigation key) | |
| [down] - down arrow | |
| [right] - right arrow | |
| [left] - left arrow | |
| [end] - "end" key | |
| [home] - "home" key | |
| [del] - delete character (the "del" key) | |
| [page up] - "page up" key | |
| [page down] - "page down" key | |
| [f1] - function key F1 | |
| [f2] - F2 | |
| [f3] - F3 | |
| [f4] - F4 | |
| [f5] - F5 | |
| [f6] - F6 | |
| [f7] - F7 | |
| [f8] - F8 | |
| [f9] - F9 | |
| [f10] - F10 | |
| In addition, "control" keys (i.e., keys entered by holding down the | |
| "control" or "ctrl" key on the keyboard and pressing an alphabetic | |
| key) are returned as "[ctrl-X]", where "X" is the lower-case letter | |
| key; and "alt" keys are returned as "[alt-X]". Finally, the "Return" | |
| and "Enter" keys are returned as "\n", and the tab key is returned | |
| as "\t". | |
| Even though these key names are portable, be aware that not every | |
| computer has all of these keys, so you can't count on the player | |
| actually being able to enter them. The only keys that you can always | |
| count on being present are the regular ASCII keys, Enter/Return, Tab, | |
| and Backspace. So, if you're using these extended keys, you should | |
| always be sure to provide an alternative for each extended key using | |
| an ordinary key. For example, if you want to implement a menu system | |
| that uses the up and down arrow keys to navigate through a set of | |
| choices, you could use "N" (for "next") and "P" (for "previous") as | |
| synonyms for [down] and [up], respectively. | |
| The arrow keys ([up], [down], [left], and [right]) are probably the | |
| most portable of the extended keys, since most computers and terminals | |
| have some sort of arrow keys. The function keys ([f1] through [f10]) | |
| are also available on many systems, although some systems use some or | |
| all of the function keys for special purposes; for example, Windows | |
| uses the F10 key to begin navigating the menu bar, so your game will | |
| never receive the [f10] extended key when running on Windows. The | |
| ALT and CONTROL keys are also very non-portable. | |
| Here's an example of using the arrow keys. | |
| numberVerb: deepverb | |
| verb = 'number' | |
| action(actor) = | |
| { | |
| local num; | |
| local done; | |
| local changed; | |
| "Press the Up or Down arrow keys, or the + or - keys, | |
| to change the number. Press Enter when finished.\b"; | |
| num := 5; | |
| changed := true; | |
| for (done = nil ; !done ; ) | |
| { | |
| if (changed) | |
| { | |
| "\nCurrent value = <<num>>"; | |
| changed := nil; | |
| } | |
| switch(inputkey()) | |
| { | |
| case '\n': | |
| done := true; | |
| break; | |
| case '+': | |
| case '[up]': | |
| ++num; | |
| changed := true; | |
| break; | |
| case '-': | |
| case '[down]': | |
| --num; | |
| changed := true; | |
| break; | |
| } | |
| } | |
| "\bThe final value was <<num>>. "; | |
| } | |
| ; | |
| - New feature: the gettime() built-in function can now return additional | |
| system real-time clock information. The function now optionally takes | |
| an argument specifying what type of information to return. Constants | |
| for the argument values are defined in adv.t: | |
| GETTIME_DATE_AND_TIME - this returns the traditional date and time | |
| information that gettime() returned in the past. This is the same | |
| information that the function returns if called with no arguments | |
| (thus ensuring that existing code that calls gettime() will continue | |
| to work unchanged). | |
| GETTIME_TICKS - this returns the number of milliseconds since an | |
| arbitrary zero point, which is usually some system event, such as | |
| starting the current session of the TADS interpreter, or turning | |
| on the computer. The actual zero point is arbitrary, but it will | |
| remain fixed for a particular session, so you can use this form | |
| of gettime() to compute relative times between events over a short | |
| period of time. For example, if you're reading events with the | |
| new inputevent() function, you can use this time value to set a | |
| limit on how long you read events. For example: | |
| local max_time, cur_time, evt; | |
| /* process events for no more than 5 seconds (5000 milliseconds) */ | |
| max_time := gettime(GETTIME_TICKS) + 5000; | |
| for (;;) | |
| { | |
| /* check to see if we've reached our time limit */ | |
| cur_time := gettime(GETTIME_TICKS); | |
| if (cur_time >= max_time) | |
| break; | |
| /* get events, but time out if we exceed our time limit */ | |
| evt := inputevent(max_time - cur_time); | |
| /* process the event */ | |
| switch(evt[1]) | |
| // and so on | |
| } | |
| - New feature: a new built-in function lets you pause the game for a | |
| specified interval. You can use this function in the middle of | |
| displaying a long text passage, for example, to create a dramatic | |
| pause effect, without making the user press a key to continue. | |
| The new function is called timeDelay(), and takes a single argument | |
| giving the number of milliseconds to pause the game. For example, | |
| to pause the game for five seconds, use this: | |
| timeDelay(5000); | |
| - The player command parser now allows a preposition to be used when | |
| answering a request for an indirect object. For example: | |
| >unlock door | |
| What do you want to unlock it with? | |
| >with key | |
| - The "oops" command (which lets the player correct a misspelled word in | |
| the previous command) now accepts multiple words to substitute for a | |
| misspelled word. This lets you correct an omitted space in a command: | |
| >take redbox | |
| I don't know the word "redbox". | |
| >oops red box | |
| Taken. | |
| - New feature: the askfile() built-in function now takes two additional, | |
| optional parameters that let you specify what type of prompt to show | |
| and what type of file to request. These new arguments are hints to | |
| the system-specific code that displays the "open file" dialog; by | |
| specifying this new information, you help the system code show the | |
| correct type of dialog. | |
| The new askfile() syntax looks like this: | |
| filename := askfile(prompt_text, prompt_type_code, file_type_code); | |
| The prompt_type_code tells the open-file dialog whether you're | |
| opening an existing file or saving a file. On some systems (Windows | |
| and Macintosh included), the user interface uses one type of dialog | |
| for opening an existing file, and a different type of dialog for | |
| saving a file; you can use this parameter to select the appropriate | |
| dialog type on systems that make this distinction. This parameter | |
| can have one of the following values, defined in adv.t: | |
| ASKFILE_PROMPT_OPEN - open an existing file for reading | |
| ASKFILE_PROMPT_SAVE - open a file for saving information | |
| On some systems, the open-file dialog will filter the files it | |
| displays so that the player only sees files of the particular type | |
| being requested. The file_type_code parameter lets you specify | |
| the type of file you're interested in, so that the dialog can use | |
| the appropriate filtering on systems that support this. The | |
| file_type_code can be one of the following values, defined in adv.t: | |
| FILE_TYPE_GAME - a game data file (.gam) | |
| FILE_TYPE_SAVE - a saved game (.sav) | |
| FILE_TYPE_LOG - a transcript (log) file | |
| FILE_TYPE_DATA - general data file (used for fopen()) | |
| FILE_TYPE_CMD - command input file | |
| FILE_TYPE_TEXT - text file | |
| FILE_TYPE_BIN - binary data file | |
| FILE_TYPE_UNKNOWN - unknown file type | |
| adv.t and std.t have been updated to specify these new arguments in | |
| all of their calls to askfile(). | |
| If you leave out the new arguments in a call to askfile(), the | |
| function will behave as it did in the past. This means that your | |
| prompt string must contain the word "save" or "write" in order to | |
| show a "save file" dialog rather than an "open file" dialog on | |
| those systems that differentiate between these dialog types. | |
| - Fixed a bug with the parserSetMe() function: on restart(), the system | |
| did not correctly restore the current "Me" object to the original | |
| object. This works properly now. | |
| - Fixed a player command parser bug that caused problems with numbers | |
| in commands under certain circumstances. In particular, if an object | |
| happened to use a number as an adjective, and the player typed a | |
| command involving the same number, but to refer to the number itself | |
| rather than to the object with the numeric adjective, the parser | |
| incorrectly assumed that the player meant to use the object rather | |
| than the number. Consider this example: | |
| >i | |
| You are carrying Chapter 3. | |
| >turn dial to 3 | |
| Previously, the parser interpreted the "3" as referring to the | |
| "chapter 3" object. This problem has been corrected; now, the | |
| parser will still try the "chapter 3" object first, but when the | |
| parser finds that the command fails the verification pass (because | |
| verIoTurn or verDoTurn display an error), and the original word | |
| used in the command was simply a number, the parser will assume | |
| that the player means to refer to the number itself rather than | |
| any object that has an adjective matching the number. | |
| - Fixed a bug in adv.t that caused misleading messages when the player | |
| was in a chairItem and tried to refer to something in the enclosing | |
| room when the chairItem's reachable list did not include items in | |
| the enclosing room. In this case, adv.t displayed the message "I | |
| don't see that here," which was clearly incorrect in that the objects | |
| in a chairItem's enclosing room are generally visible even when not | |
| reachable. | |
| To correct this, the room object in adv.t now calls a new method | |
| from its cantReach() method when the actor is not directly in the | |
| room. The new method is cantReachRoom(); room.cantReach() calls | |
| actor.location.cantReachRoom(self) to display an appropriate | |
| message. In other words, if the actor is in a chair within a room, | |
| adv.t will call the chair's cantReachRoom() method with the chair's | |
| enclosing room as the argument. | |
| adv.t defines a default cantReachRoom() method for the classes thing, | |
| room, and chairItem. These default methods all generate similar | |
| messages saying roughly "You can't reach that from here." | |
| Note that the new cantReachRoom() method provides new versatility for | |
| situations where objects are visible from a location but not reachable. | |
| For example, if you want to create two rooms separated by a glass | |
| partition, so that objects in one room are visible from the other | |
| room but not reachable, you can override isVisible and getVisibleList | |
| for the rooms to make the contents of the other room visible from | |
| each room, and then add a cantReachRoom(otherRoom) method like this: | |
| cantReachRoom(otherRoom) = | |
| { | |
| if (otherRoom = eastSideOfGlassRoom) | |
| "That's on the other side of the glass partition. "; | |
| else | |
| inherited.cantReachRoom(otherRoom); | |
| } | |
| - Fixed a bug in the player command parser that caused the parser to | |
| repeatedly say "I don't know the word <word>" when an invalid word | |
| was entered in a command *and* the preparseCmd function modified | |
| the command by returning a list value. In this situation, the | |
| parser would not stop saying "I don't know the word <word>" until | |
| the player entered an "oops" command to fix the misspelled word. | |
| This no longer occurs. | |
| - The parser responded incorrectly to unknown words entered in | |
| response to a disambiguation question ("which box do you mean, the | |
| red box, or the blue box?"). If the player entered an unknown word | |
| in response to such a question, the parser responded by asking the | |
| same question over again, without mentioning that the word was | |
| unknown. The parser now correctly mentions the unknown word. | |
| Note that this change removes a certain amount of control from the | |
| game's processing of unknown words through parseUnknownXobj, since | |
| these methods are not called when unknown words are entered in | |
| response to disambiguation questions. | |
| - Fixed a parser bug that caused errors for certain valid commands | |
| involving an indirect object that used an adjective that also was | |
| used as a preposition. For example, if the game had a "south wall" | |
| object, the parser did not correct interpret the command "put box | |
| on south wall" (the response was "I don't understand that sentence"). | |
| This has been corrected. | |
| - Numerous changes in adv.t make the default messages more consistent | |
| at using parameterized pronouns and verbs to refer to objects. These | |
| changes generally make the default messages agree more automatically | |
| in number and gender with the objects described. The parameterized | |
| messages are mostly keyed on the isThem property; if you have an | |
| object that's described by a plural noun phrase ("the pants" or | |
| "some marbles," for example), setting isThem = true in the object | |
| will help adv.t generate the correct default messgaes when referring | |
| to the object ("you can't put the marbles in themselves," for example). | |
| Many thanks to Stephen Granade for his extensive work to fix these. | |
| - adv.t is now somewhat more consistent in its handling of reachability | |
| from nested rooms. In the past, chairItem objects and other nestedroom | |
| objects had a strange difference: from a normal nestedroom, you could | |
| reach anything in the nestedroom's "reachable" list, plus anything in | |
| any open containers in the list, to any nesting level; in contrast, | |
| from a chairItem, you could only reach the objects directly in the | |
| chairItem's "reachable" list. | |
| For compatibility with past versions, this behavior hasn't changed. | |
| However, it's now simple to make the behavior the same for both types | |
| of rooms. If you want chairItem objects to behave the same way that | |
| all other nestedroom objects behave, simply modify chairItem and set | |
| its "canReachContents" property to true: | |
| modify chairItem | |
| canReachContents = true | |
| ; | |
| This is probably the most logical behavior, so authors of new games | |
| may want to consider including this change. However, the default | |
| remains for chairItem objects to limit reachability to the items | |
| directly in the "reachable" list with no access to their contents, | |
| so that any existing games that depended (intentionally or otherwise) | |
| on the old behavior are not broken by an incompatible change. | |
| - Fixed a bug that caused a spurious error (TADS-601, "error writing | |
| to game file") when attempting to create a pre-compiled header file, | |
| when a #define symbol was defined to an empty string. This problem | |
| has been corrected. | |
| - The compiler went into an infinite loop reporting an error | |
| (TADS-353, "'local' is only allowed at the beginning of a block") | |
| when a misplaced "local" statement appeared. This is fixed; the | |
| compiler now simply ignores the entire "local" statement and | |
| continues attempting to parse the code. | |
| - The random number generator caused a divide-by-zero error (which | |
| could abruptly terminate the interpreter on some platforms) when | |
| the game called rand(0) at any time after calling randomize(). | |
| This has been corrected; rand(0) now simply returns 0 without | |
| causing a crash. | |
| - In adv.t, the clothingItem code was written so that, if the player | |
| attempted to wear something that another actor was wearing, the | |
| game responded with "You're already wearing that," which clearly | |
| isn't correct. The game now says "You don't have that," which makes | |
| more sense. | |
| ------------------------------------------------------------------------------ | |
| 2.2.6 09/30/98 bug fixes | |
| - The release notes are now separated into a generic section and a | |
| platform-specific section. This file (TADSVER.TXT) describes the | |
| changes that apply to all types of computers and operating systems. | |
| A separate file describes the changes specific to each platform; | |
| for example, DOSVER.TXT describes the changes that apply only to | |
| the MS-DOS and Windows versions of TADS. | |
| - adv.t now has a "knock" verb (knock, knock on, and knock at are | |
| all synonyms). The "doorway" class provides has a default doKnock | |
| method that simply displays "there is no answer" in response to | |
| the player knocking on the door. | |
| - adv.t now has a "go through" verb. The "doorway" class provides a | |
| default handler for "go through" that has the same effect as the | |
| player traveling in the direction of the door. | |
| - In adv.t, the "doorway" class now has a default handler for the | |
| "enter" verb that has the same effect as the player traveling in | |
| the direction of the door. | |
| - In adv.t, the default message for eating a "thing" now correctly | |
| incorporates the ordinality of the object, and the verb agrees | |
| with the subject (so "the tire doesn't appear appetizing" whereas | |
| "the keys don't appear appetizing"). | |
| - In the adv.t "doorway" class, the setIsopen method now calls | |
| setIsopen on the object representing the other side of the door, | |
| rather than updating its isopen property directly. In addition, the | |
| code that automatically opens the door when the player attempts to | |
| travel through the door calls setIsopen rather than updating the | |
| isopen properties of the door object and the other side object | |
| directly. These change make it easier to code a door that has | |
| special behavior when opened or closed, since all changes to the | |
| isopen property made by code in the doorway class now occur through | |
| the setIsopen method, so all of the special behavior code can be | |
| placed in an overridden setIsopen method. | |
| - The player command parser did not correctly respond to disambiguation | |
| questions (such as "which box do you mean, the green box, or the | |
| red box?"). In some cases, when the player ignored the question and | |
| simply typed a new command, the parser completely ignored the command | |
| and show a new command prompt with no other comment; alternatively, | |
| the parser responded to the new command as though it were an object, | |
| showing a message such as "I don't see any open door here." In yet | |
| other situations, when the player attempted to answer the question | |
| with a more detailed object description, the parser would complain | |
| that it didn't see such an object even though the additional player | |
| input was correct. All of these problems have been corrected; the | |
| parser should handle player responses to disambiguation questions | |
| correctly now. | |
| - A bug in the player command parser caused an infinite loop under | |
| certain conditions. The loop occurred if one of an object's nouns | |
| was a word that was also used as a verb, and the player typed a | |
| command of the form "<verb> <noun> of <noun>", where <verb> is | |
| the word used as both a noun and a verb. For example, the command | |
| "discard ace of spades" caused the loop if "discard" was defined as | |
| a verb and was also used as a noun for some object. This has been | |
| corrected. | |
| - The compiler did not correctly handle the error "else without if". | |
| Rather than ignoring the offending "else" keyword, the compiler got | |
| stuck in an infinite loop generating the same error message over and | |
| over. This has been corrected; the compiler now only generates the | |
| error message once per occurrence of the error in the source code. | |
| - The compiler did not correctly handle the -case- option. Although | |
| games compiled correctly, they did correctly handle verbs with objects | |
| at run-time. This has been corrected. | |
| - The compiler did not correctly interpret expressions of this form: | |
| (x ? "first string" : "second string << 5 >> end of second string") | |
| In particular, when a string containing an embedded expression was | |
| used as the third operand of a ternary conditional operator (the "?:" | |
| operator), the compiler interpreted the embedded expression and | |
| everything that followed as an entirely separate expression, which | |
| was incorrect. The compiler now handles these expressions correctly. | |
| - The compiler did not correctly interpret a statement that consisted | |
| entirely of a parenthesized expression when used as the "true" branch | |
| of an "if" statement when an "else" clause was present: | |
| if (expr) | |
| (1+2+3); | |
| else | |
| ... | |
| The compiler incorrectly generated an "else without if" error in this | |
| situation, because it incorrectly ignored the semicolon that followed | |
| the statement. This has been corrected; the semicolon is now parsed | |
| properly. Note that if your game inadvertantly exploited this error | |
| by omitting semicolons in such cases, you will have to change your | |
| source code; because of the obscurity of this case we consider it | |
| extremely unlikely that anyone will be affected, but if you are, you | |
| will receive "expected semicolon" messages at the erroneous lines of | |
| code. | |
| - A compiler bug caused crashes when using __LINE__ under certain | |
| circumstances; this bug has been fixed. | |
| - The interpreter now displays a separate error message (TADS-617) when | |
| it attempts to load an external resource file (.RS0, etc) with an | |
| invalid header. The interpreter previously displayed the message for | |
| an invalid game file, which was not helpful in tracking down the | |
| problem. The error message for TADS-617 displays the name of the | |
| resource file causing the error, to help pinpoint the problem. | |
| ------------------------------------------------------------------------------ | |
| 2.2.5 08/24/98 enhancements and bug fixes | |
| - New parser feature: The parser now calls a new pair of methods on | |
| the direct and indirect object in the course of processing each | |
| command. The new methods are called dobjCheck and iobjCheck, and | |
| are called with these parameters: | |
| iobjCheck(actor, verb, dobj, prep) | |
| dobjCheck(actor, verb, iobj, prep) | |
| iobjCheck is called on the indirect object, if any, just before | |
| iobjGen is or would be called. (iobjCheck is always called, whether | |
| or not iobjGen is called.) Similarly, dobjCheck is called on the | |
| direct object just before dobjGen is or would be called. Note that | |
| the dobj, iobj, and prep parameters may be nil if the corresponding | |
| objects are not present in the command. | |
| These new methods are very similar to dobjGen and iobjGen, but have | |
| one difference: the new methods are ALWAYS called. This differs from | |
| dobjGen and iobjGen, which are called only when the object does not | |
| define the corresponding verb handler method (do<Verb> or io<Verb>) | |
| for the verb being used. | |
| The new dobjCheck and iobjCheck methods are called immediately | |
| BEFORE the corresponding dobjGen and iobjGen methods are called. | |
| So, the new calling sequence looks like this: | |
| actor.actorAction | |
| actor.location.roomAction | |
| *NEW* iobj.iobjCheck (if there's an indirect object) | |
| iobj.iobjGen (if the indirect object doesn't define io<Verb>) | |
| *NEW* dobj.dobjCheck (if there's a direct object) | |
| dobj.dobjGen (if the direct object doesn't define do<Verb>) | |
| and then on to the normal verIo<Verb> and/or verDo<Verb>, as | |
| appropriate. | |
| So, why have both xobjGen and xobjCheck? The reason is that each | |
| is useful in its own way. | |
| The xobjGen routines are meant as *default* handlers. They're | |
| catch-all methods that handle any command that isn't specially | |
| handled by the object. Because any normal verb handler (one of the | |
| io<Verb> or do<Verb> routines) "overrides" the xobjGen routines, it's | |
| not necessary to include a special check in the xobjGen method for | |
| the verbs that the object handles in a non-default fashion. | |
| The xobjCheck routines, on the other hand, are mandatory checks that | |
| are applied to all verbs used with an object, whether or not the verb | |
| is otherwise handled by the object. This makes these routines useful | |
| in cases when the object's behavior can change, because you can perform | |
| tests based on the object's state in a single place for all verbs, | |
| rather than having to apply the same test in every verb handler. | |
| - A bug in past versions handled doSynonym and ioSynonym incorrectly | |
| when they were used in classes. Consider the following example: | |
| class draggableItem: fixeditem | |
| verDoPull(actor) = { } | |
| doPull(actor) = | |
| { | |
| "You manage to drag <<self.thedesc>> along the ground | |
| for a few feet. "; | |
| // etc... | |
| } | |
| doSynonym('Pull') = 'Push' 'Move' | |
| ; | |
| desk: draggableItem | |
| // noun, location, sdesc, etc... | |
| doPull(actor) = | |
| { | |
| "The desk scrapes along the floor for a few feet, | |
| but everything falls off! "; | |
| // etc... | |
| } | |
| ; | |
| Now, if the player typed PULL DESK, the result would be as you'd | |
| expect: "the desk scrapes along the floor for a few feet, but | |
| everything falls off!" | |
| However, if the player typed PUSH DESK, you'd get the unexpected | |
| reply "You manage to drag the desk along the ground for a few feet." | |
| The problem was that the system was invoking the synonym routine for | |
| the definition in the class defining the synonym, rather than properly | |
| using the overriding method in the actual object used in the command. | |
| This problem has been corrected. | |
| - A player command parser bug allowed using AGAIN to repeat a command | |
| directed to an actor after the actor had left the room (or otherwise | |
| become inaccessible). The parser did not check to ensure that the | |
| actor was still present for the repeated command. This has been | |
| corrected; the parser now checks that the actor is still accessible | |
| when using AGAIN. | |
| - A bug in the player command parser, introduced with the parseUnknownXobj | |
| feature added in version 2.2.4, caused the parser to fail to display | |
| proper default prompts when the player entered a command with an | |
| unknown word that also required an additional object (for example, | |
| if the player typed "put foo", where "foo" was not a valid vocabulary | |
| word). This has been corrected; the parser now simply reports the | |
| unknown word in these cases. | |
| - A bug in the compiler caused a subtle problem when 'modify' was | |
| used to modify a class where the original class had vocabulary | |
| defined, but the 'modify' did not add any new vocabulary. In such | |
| cases, the vocabulary defined in the original class was lost, so | |
| any objects derived from the class did not have the original class | |
| vocabulary words. This problem has been corrected. | |
| Several people encountered this problem when using 'modify' with | |
| the 'basicMe' class defined in adv.t. In these cases, the bug | |
| prevented the 'Me' object from inheriting the nouns from the | |
| original 'basicMe' in adv.t, so at run-time a command such as | |
| "look at me" would not work properly. (The work-around, which | |
| was to add a noun='' definition to the modified object, is no | |
| longer necessary now that the bug has been fixed, but should not | |
| cause any problems.) | |
| - When restoring a saved game directly from the command line when | |
| starting the run-time, the run-time incorrectly invoked the init() | |
| function before invoking the initRestore() function. Since the | |
| initRestore() function is meant to be called *instead of* the init() | |
| function in this situation, this was incorrect behavior. This | |
| problem has been fixed; the run-time now invoked *either* init() | |
| or initRestore(), but not both, depending on whether the game is | |
| started normally or with a restored game on the command line. This | |
| problem affected both the character-mode and HTML run-time versions. | |
| - A bug in the character-mode run-time caused HTML markups to be | |
| misinterpreted if closely preceded by a percent sign that wasn't | |
| being used with a format string (such as "%You%"). In such cases, | |
| the character-mode run-time sometimes displayed any tags that | |
| closely followed the percent sign (within about forty characters | |
| of output), rather than interpreting them and removing them from | |
| the display as it normally would. This has been corrected. Note | |
| that this problem affected only the character-mode run-time when | |
| operating with an HTML-enabled game. | |
| - The run-time did not correctly handle backslashes in text captured | |
| by the outcapture() built-in function; extra backslashes were | |
| unnecessarily (and incorrectly) added to escape backslashes. This | |
| has been corrected. | |
| - The run-time was incompatible with games compiled with version of | |
| the compiler before 2.2.0. This incompatibility normally resulted | |
| in the game ignoring most commands. The problem has been corrected, | |
| so old games should work correctly again. | |
| - The character-mode run-time now recognizes the <TITLE> tag when the | |
| game is operating in HTML ("\H+") mode. The character-mode version | |
| simply suppresses all text displayed between the <TITLE> and | |
| corresponding </TITLE> tags. | |
| - The character-mode run-time now allows hexadecimal values to be | |
| specified in numeric entity markups ("&#nnn"). This change is for | |
| compatibility with the HTML TADS run-time, which allows such markups. | |
| - The character-mode run-time did not always break lines correctly in | |
| text that switched in and out of HTML mode (using \H+ and \H-). This | |
| has been corrected. Note that this problem only affected the | |
| character-mode run-time (TR32), not the HTML run-time. | |
| - The dialItem object in adv.t now has a minsetting property, which | |
| specifies the minimum setting for the dial; the default value is 1. | |
| ------------------------------------------------------------------------------ | |
| 2.2.4 07/20/98 enhancements and fixes | |
| - The TADS language has a new construct that allows you to write a method | |
| so that it inherits explicitly from a particular superclass. This new | |
| syntax adds to the existing 'inherited' syntax, which allowed you to | |
| inherit from the superclass according to rules that TADS uses to resolve | |
| the superclass that a particular method overrides. | |
| The new syntax lets you specify the name of the superclass after the | |
| 'inherited' keyword, but is otherwise similar to the normal 'inherited' | |
| syntax: | |
| inherited fixeditem.doTake(actor); | |
| This specifies that you want the method to inherit the doTake | |
| implementation from the fixeditem superclass, regardless of whether | |
| TADS might normally have chosen another superclass as the overridden | |
| method. This is useful for situations involving multiple inheritance | |
| where you want more control over which of the base classes of an object | |
| should provide a particular behavior for the subclass. | |
| - adv.t now includes all of the bugs fixes that Stephen Granade assembled | |
| in his BUGS.T file. If you've been using BUGS.T, you should be able to | |
| get the same effect by compiling with adv.t alone now. I'd like to thank | |
| Stephen for creating this excellent improvement to the original adv.t. | |
| - The parser now provides a way to change the player character dynamically | |
| during game play. | |
| In the past, the parser used the object named "Me" to represent the | |
| player character; there was no way to change this. This made it | |
| difficult to write a game with different personas for the player | |
| character. | |
| The parser still uses "Me" as the initial player character, but you | |
| can now switch the player character to a different object at any | |
| time using the new built-in function parserSetMe(newMe). The argument | |
| to parserSetMe() is the new object to use to represent the player | |
| character. | |
| Another new built-in function, parserGetMe(), allows you to get the | |
| parser's current player character object. This function takes no | |
| arguments. | |
| Note that adv.t and std.t no longer refer to the "Me" object directly | |
| in code related to the player character's status (such as inventory | |
| or room location descriptions). Instead, adv.t and std.t use the new | |
| parserGetMe() function to get the player character object. | |
| If you use parserSetMe() in your game, you should be careful not to | |
| refer to the "Me" object directly in contexts where you really want | |
| the current player character object; use parserGetMe() instead to get | |
| the correct object from the parser. Note that existing games should | |
| not be affected by this change; if you don't call parserSetMe(), then | |
| parserGetMe() will always return the "Me" object, so you can safely | |
| use a fixed "Me" object. | |
| IMPORTANT NOTE: this feature may be somewhat incomplete, in that | |
| additional adv.t support may be needed for some games to take full | |
| advantage of this feature. If you try using this feature in your game | |
| and you experience any problems or have suggestions on adv.t changes | |
| that you would help you use this feature, please contact TADS's author | |
| by email at mjr_@hotmail.com. | |
| - The compiler is now more tolerant of non-standard newline conventions | |
| used in source files. MS-DOS, Macintosh, and Unix each have different, | |
| incompatible conventions for how text files represent line endings | |
| (and it's likely that there are a few other operating systems with | |
| even more different conventions). When copying a file from one | |
| type of computer to another, the newline conventions aren't always | |
| correctly translated. In the past, the TADS compiler was sometimes | |
| unable to process a file that did not use the correct newline | |
| conventions for the machine running the compiler. The compiler can | |
| now accept most combinations of line ending conventions on any | |
| platform that runs the compiler. | |
| - The player can now specify a saved game to restore directly from the | |
| run-time command line. Use the new -r argument to specify the name | |
| of the saved game to be restored: | |
| tr -r mygame.sav | |
| Note that the full filename must be provided; no default suffix is | |
| applied to the -r argument. | |
| TADS now stores the name of the .GAM file in each saved game file, | |
| and uses this information with the -r option. If you start the | |
| run-time, and you specify a saved game file to restore using -r but | |
| do not specify a game file, the TADS run-time attempts to load the | |
| game file named in the saved game file. You can always specify the | |
| name of the game file explicitly, in which case TADS will ignore the | |
| game file name stored in the save file: | |
| tr -r mygame.sav deep.gam | |
| When you use the -r option, TADS loads the game, and then immediately | |
| restores the save file. | |
| Game authors: note that you can customize the way this new feature | |
| works by using the initRestore function; see below for details. | |
| - The system now calls a new optional game-defined function named | |
| initRestore() if the player specifies a game to restore on the | |
| run-time command line. initRestore() is called with a single-quoted | |
| string argument giving the name of the saved game file to be restored. | |
| If initRestore() is defined, and the player restores a game using | |
| the -r option, the system will *not* call your game's init() function. | |
| The reason that TADS skips init() in this case is that the player | |
| will normally want to skip your game's introductory text when jumping | |
| directly to a saved position at the start of the game. (Note, however, | |
| that your preinit() function will always run as usual.) | |
| If initRestore() is not defined in your game, TADS will simply call | |
| the init() function as usual, and will then restore the game itself. | |
| This provides compatibility with older games, although it may result | |
| in a confusing initial display for the player, because TADS will not | |
| be able to show the current location immediately after restoring the | |
| game. If possible, you should define initRestore() in your game. | |
| std.t provides a default implementation of initRestore() that simply | |
| restores the game, and then shows a full description of the current | |
| location. If you use std.t in your game, you should be aware that | |
| your init() function will not be called when the player restores a | |
| game explicitly from the run-time command line, and make any | |
| necessary adjustments to your init() routine and the initRestore() | |
| routine defined in std.t. | |
| If you have code in your init() function that sets up any variables, | |
| or if you enter HTML mode in your init() function, you should be sure | |
| to call the same code from your initRestore() function, because init() | |
| will not be called in this case. The best way to structure your | |
| initialization code is to break out the common initialization code | |
| into a separate function, and call this function from your normal | |
| init() routine as well as your initRestore() routine: | |
| initCommon: function | |
| { | |
| /* set up HTML mode */ | |
| "\H+"; | |
| "<body bgcolor=purple text=fuchsia>"; | |
| } | |
| init: function | |
| { | |
| /* perform common initialization */ | |
| initCommon(); | |
| /* display opening text messages */ | |
| "A long time ago in a cavern far, far away..."; | |
| } | |
| initRestore: function(fname) | |
| { | |
| /* perform common initialization */ | |
| initCommon(); | |
| /* restore the game */ | |
| mainRestore(fname); | |
| } | |
| - To facilitate the new initRestore function, adv.t now isolates the | |
| game restoration code that was previously in the restoreVerb object | |
| in the new mainRestore() function. You can call this function from | |
| your own initRestore() routine, if you want the normal game restore | |
| functionality. mainRestore() takes a single argument, which is a | |
| single-quoted string giving the name of the save file to restore. | |
| mainRestore() restores the saved game, then updates the status line | |
| and displays a full description of the current location. | |
| - Just as the system calls the game hook function commandPrompt prior | |
| to reading a command, the system now calls a new game hook function, | |
| commandAfterRead, immediately after reading a player command. The | |
| new function looks like this: | |
| commandAfterRead: function(code) | |
| where 'code' is the same prompt type code that is passed to the | |
| corresponding call to commandPrompt. This new function is intended | |
| to make it easier to customize certain aspects of your game's user | |
| interface. In particular, if you're using special formatting for | |
| the player command in the HTML run-time (for example, you want to | |
| use a special typeface or font color for player commands), you can | |
| use commandAfterRead to turn off the special formatting you turned | |
| on in commandPrompt. Each call to commandPrompt is matched by an | |
| equivalent call to commandAfterRead, so you can always count on | |
| being able to undo any formatting changes you make in commandPrompt | |
| by placing the matching formatting commands in commandAfterRead. | |
| - A new parser hook provides you with more flexibility in determining | |
| how unknown words in a player command are handled. The new parser | |
| hook operates during noun phrase resolution (this is the process | |
| by which the parser attempts to determine what object should be used | |
| for each noun phrase entered by the player in a command). | |
| In the past, the parser detected unknown words (i.e., words not | |
| defined as vocabulary properties, such as 'noun' or 'adjective', | |
| somewhere in the game program) during the initial dictionary lookup | |
| step of command processing. Upon encountering an unknown word at | |
| this step, the parser simply reported the error ("I don't know the | |
| word <word>") and aborted the command. | |
| The parser now marks unknown words found during the dictionary lookup | |
| step, but doesn't generate an error at this step. Instead, it | |
| tentatively considers such words to be parts of noun phrases, and | |
| continues parsing. The parser then attempts to determine the verb | |
| and general structure of the command as normal; if this fails, the | |
| parser goes back and reports the unknown word as it used to. However, | |
| if the parser is able to find a valid sentence structure, it continues | |
| to the next step, which is noun phrase resolution. | |
| Note that the parser considers unknown words to have a completely | |
| neutral part of speech, which means that unknown words are merged with | |
| any known nouns and adjectives to which they are adjacent to form the | |
| noun phrase. Since the parser doesn't know the word, it can't decide | |
| whether to treat it as a noun or adjective, so it simply considers it | |
| to be neutral and allows it to combine with whatever other words are | |
| present. | |
| Noun phrase resolution proceeds as normal until the parser once again | |
| encounters one of the unknown words. So, the parser will call the | |
| verification (verDoXxx) and validation (validDo, validDoList) | |
| routines as usual for any noun phrases containing recognized words, | |
| in the usual order (for most verbs, this means that the indirect | |
| object is resolved first, then the direct objects). | |
| Upon encountering an unknown word, however, the parser checks to see | |
| if your game program defines the new parser hook. The new hook is a | |
| method on the deepverb object called parseUnknownDobj (to resolve | |
| direct objects) or parseUnknownIobj (to resolve indirect objects). | |
| The methods are called with these parameters: | |
| parseUnknownDobj(actor, prep, iobj, wordlist) | |
| parseUnknownIobj(actor, prep, dobj, wordlist) | |
| In both cases, 'actor' is the actor object to which the command is | |
| directed; 'prep' is the preposition that introduces the indirect | |
| object, or nil if there is no preposition; and 'wordlist' is a list | |
| of single-quoted strings containing all of the words -- both known | |
| and unknown -- making up the noun phrase that contains the unknown | |
| word or words. | |
| The 'iobj' argument in parseUnknownDobj is the indirect object, if | |
| present and known; similarly, the 'dobj' argument in parseUnknownIobj | |
| is the direct object, if present and known. The 'iobj' or 'dobj' | |
| argument will be nil if there was no other object in the command, or | |
| if that object has not been resolved by the time this method is called. | |
| When the indirect object is resolved first, as it is with most verbs, | |
| parseUnknownIobj will always receive nil for the direct object, since | |
| the direct object cannot be resolved until the indirect object is | |
| resolved, which is what parseUnknownIobj is doing. | |
| These routines can return the following values: | |
| nil - This indicates that the routine did not successfully resolve | |
| the object, and the system should use the default handling. The | |
| parser reports the unknown word error as usual. | |
| true - This indicates that the routine has fully processed the | |
| command for this noun phrase, and no further parser action is | |
| necessary. The parser simply continues processing any other | |
| objects in the command, but treats this noun phrase as having | |
| no corresponding objects in the command and does no further | |
| processing on it. | |
| object - The routines can return an object value, which the parser | |
| treats as the resolution of the noun phrase. The parser proceeds | |
| to apply all normal processing to the object as though the parser | |
| had found an appropriate set of dictionary words for the noun | |
| phrase matching the returned object. So, all of the normal | |
| verification, validation, and action routines are called for the | |
| object. | |
| list - The routines can return a list containing object values. | |
| The parser uses *all* of the objects in the list as the resolution | |
| of the noun phrase, and applies all of the normal processing to | |
| each object in the list. The effect is the same as if the user | |
| had entered the objects in the command separated by commas. | |
| If your game doesn't define this new method for the verb, the parser | |
| simply uses the default handling, and reports the unknown word error. | |
| Here's a simple example that changes the ASK ABOUT command so that | |
| we always let the actor respond, even if a word that the player is | |
| asking about isn't defined anywhere in the game. To accomplish this, | |
| we add a parseUnknownIobj to askVerb. This routine will return a | |
| special object, unknownAskIobj, that we define solely as a placeholder | |
| for ASK ABOUT with an unknown word. We'll set unknownAskIobj's | |
| wordlist property to the list of unknown words, and the object uses | |
| the list of words to construct its sdesc. | |
| #pragma C- | |
| /* | |
| * Special object that we use as the indirect object of any | |
| * ASK ABOUT command that refers to unknown words | |
| */ | |
| unknownAskIobj: thing | |
| wordlist = [] | |
| sdesc = | |
| { | |
| local i; | |
| for (i := 1 ; i <= length(wordlist) ; ++i) | |
| { | |
| if (i != 1) | |
| " "; | |
| say(self.wordlist[i]); | |
| } | |
| } | |
| ; | |
| /* | |
| * For "ask about," use special handling for unknown words so | |
| * that the actor can respond directly to the unknown words. | |
| */ | |
| modify askVerb | |
| parseUnknownIobj(actor, prep, dobj, words) = | |
| { | |
| /* if we're asking about something, have the actor respond */ | |
| if (prep = aboutPrep) | |
| { | |
| /* use our special ASK ABOUT object for the unknown words */ | |
| unknownAskIobj.wordlist := words; | |
| return unknownAskIobj; | |
| } | |
| else | |
| { | |
| /* | |
| * it's not ASK ABOUT, return nil to use the | |
| * default system handling | |
| */ | |
| return nil; | |
| } | |
| } | |
| ; | |
| - The compiler can now capture all of the strings used in a game to a | |
| text file. This can be useful for spell-checking your game, since | |
| it gives you a listing of all of the text in the game, separated | |
| from your source code. To capture strings during compilation, use | |
| the -Fs option to specify the output file: | |
| tc -Fs mygame.lis mygame.t | |
| This compiles mygame.t, producing mygame.gam as usual, and writes | |
| all of the strings in the source code to the file mygame.lis. | |
| - The character-mode run-time now provides improved, but still limited, | |
| HTML support. As in version 2.2.3, after you display an "\H+" | |
| sequence, the character-mode run-time becomes sensitive to HTML tag | |
| and character markup sequences. However, whereas version 2.2.3 | |
| ignored all tags and ampersand sequences, the new version now obeys | |
| certain markups. In particular, the character-mode run-time will now | |
| process the following tags: | |
| <br> ends the current line; additional <br>'s display blank lines. | |
| The HEIGHT attribute is accepted, and produces results that | |
| are consistent with the graphical handling. | |
| <p> displays a blank line. | |
| </p> displays a blank line | |
| <b> and <em> start boldface mode. | |
| </b> and </em> end boldface mode. | |
| <tab> indents to the next tab stop (exactly like "\t") | |
| <img> and <sound> accept the ALT attribute, and display the text | |
| of the ALT attribute value in place of the image or sound. | |
| No other decoration is added; the ALT value is simply included | |
| in the text display as though it had appeared as ordinary text. | |
| <hr> starts a new line, displays a line of dashes, and starts | |
| another new line | |
| The character-mode run-time ignores all other tags. As before, | |
| unrecognized tags are simply removed from the text entirely. | |
| In addition, the ampersand character-code markups are now supported. | |
| Since the DOS character set does not contain all of the characters in | |
| the HTML character set (ISO Latin 1), some ampersand markups are | |
| displayed as blanks, and others are displayed as approximations. | |
| The following characters are displayed correctly: | |
| &endash; | |
| &emdash; | |
| ¡ | |
| ¢ | |
| £ | |
| ¥ | |
| ¦ | |
| ª | |
| « | |
| ¬ | |
| ° | |
| ± | |
| ² | |
| µ | |
| · | |
| º | |
| » | |
| ¼ | |
| ½ | |
| ¿ | |
| Ä | |
| Å | |
| Æ | |
| Ç | |
| É | |
| Ñ | |
| Ö | |
| Ü | |
| à | |
| á | |
| â | |
| ä | |
| å | |
| æ | |
| ç | |
| è | |
| é | |
| ê | |
| ë | |
| ì | |
| í | |
| î | |
| ï | |
| ñ | |
| ò | |
| ó | |
| ô | |
| ö | |
| ÷ | |
| ù | |
| ú | |
| û | |
| ü | |
| ÿ | |
| The following are displayed using approximations: | |
| „ is displayed as a normal double-quote | |
| ‹ is displayed as '<' | |
| ‘ is displayed as a normal single-quote | |
| ’ is displayed as a normal single-quote | |
| “ is displayed as a normal double-quote | |
| ” is displayed as a normal double-quote | |
| › is displayed as '>' | |
| ­ is displayed as '-' | |
| ´ is displayed as a normal single-quote | |
| ¸ is displayed as a comma | |
| × is displayed as an 'x' | |
| ‚ is displayed as a normal single-quote | |
| ß is displayed as a "beta" | |
| ™ is displayed as "(tm)" | |
| © is displayed as "(c)" | |
| The following are displayed using the corresponding unaccented | |
| character, since the accented version of the character is not in | |
| the DOS character set: | |
| Ÿ | |
| À | |
| Á | |
| Â | |
| Ã | |
| È | |
| Ê | |
| Ë | |
| Ì | |
| Í | |
| Î | |
| Ï | |
| Ò | |
| Ó | |
| Ô | |
| Õ | |
| Ø | |
| Ù | |
| Ú | |
| Û | |
| Ý | |
| ã | |
| õ | |
| ø | |
| ý | |
| The following are not supported at all, since the DOS character set | |
| has no equivalents for these characters. Each of these markups is | |
| rendered as a single space. | |
| † | |
| ‰ | |
| Œ | |
| œ | |
| ¤ | |
| § | |
| ¨ | |
| ® | |
| ¯ | |
| ³ | |
| ¶ | |
| ¹ | |
| ¾ | |
| Ð | |
| Þ | |
| ð | |
| þ | |
| - The new built-in function morePrompt() allows your game to explicitly | |
| display the system MORE prompt. You can use this for such special | |
| effects as a dramatic pause prior to a chapter change. The new | |
| function takes no arguments and returns no value. | |
| - The built-in file manipulation functions now allow you to read and | |
| write text-mode files. Files written in text mode can be used by | |
| other applications as ordinary text files. | |
| To use text-mode files, you must specify the new 't' file mode | |
| suffix in fopen (for symmetry, a new 'b' mode suffix, for binary | |
| mode files, is also allowed, but for compatibility with past | |
| versions, binary mode is the default if no mode suffix is | |
| specified). You can use the 't' suffix with 'r' (read) and 'w' | |
| (write) modes; 't' is not currently allowed with 'r+' or 'w+' | |
| modes. | |
| When a file is opened in text mode, fwrite() can only be used with | |
| string values. Strings passed to fwrite() can contain the escape | |
| characters '\t', '\n', and '\\'; other escapes are not allowed. | |
| '\t' is translated to a tab, '\n' is translated to a newline (using | |
| the appropriate local conventions for the current system), and '\\' | |
| is translated to a single backslash. fwrite() does NOT add any | |
| newlines to the text you provide, so you must explicitly include | |
| any newlines you want to write to the file. | |
| Because TADS obeys local newline conventions, fwrite() always | |
| produces the correct sequence of characters for the current machine | |
| when you include '\n' in a string, so you don't have to worry about | |
| how newlines are handled on each platform. | |
| fread() always reads a line of text from the file. If the end of | |
| the file is not reached, the line returned will end with a '\n' | |
| sequence (as with fwrite(), fread() translates newlines according | |
| to local conventions, and always returns the TADS '\n' sequence to | |
| represent a newline in the file). If fread() encounters the end | |
| of the file in the middle of a line, it will return the text up | |
| to the end of the file, with no trailing newline. The subsequent | |
| call will return nil to indicate that the end of the file has been | |
| reached. | |
| - Because TADS now has several ways of reading and writing files, | |
| some game players may be uncomfortable about the possibility that | |
| a malicious game author could harm their systems by writing a game | |
| that, for example, modifies their AUTOEXEC.BAT files. To address | |
| any concerns that players may have, TADS now provides a "file safety | |
| level" setting. This is an optional setting that allows the player | |
| to control the amount of access that a game has to files on the | |
| system at run-time. | |
| The file safety level is set through a command line option (note | |
| that HTML TADS also provides this setting through the "Preferences" | |
| dialog). Use the new -s option to specify one of the possible | |
| safety levels: | |
| -s0 (default) minimum safety - read and write in any directory | |
| -s1 read in any directory, write in current directory | |
| -s2 read-only access in any directory | |
| -s3 read-only access in current directory only | |
| -s4 maximum safety - no file I/O allowed | |
| If the game attempts a file operation that is not allowed by the | |
| current safety level, the fopen() function returns nil to indicate | |
| that the file open failed. | |
| These options affect only explicit file I/O operations performed by | |
| the game. Operations handled by the system, such as saving and | |
| restoring games and logging a transcript to a file, are not affected | |
| by the file safety level setting. | |
| - The TADS resource manager (TADSRSC) now supports building external | |
| resource files for HTML TADS games. Refer to the resource file | |
| documentation (RES.HTM) that accompanies the HTML TADS distribution | |
| for details. | |
| - The new TADS built-in function systemInfo() allows your game to | |
| determine programmatically whether the TADS run-time that is | |
| currently executing the game has certain capabilities. This | |
| function is called like this: | |
| result = systemInfo(__SYSINFO_xxx); | |
| where __SYSINFO_xxx is one of the pre-defined constants (defined | |
| automatically by the compiler) listed below. The result tells | |
| you about the particular run-time that is executing your game, | |
| so you can customize the game, if you wish, for certain system | |
| capabilities. For example, you might want to change some text | |
| in your game slightly depending on whether sound effects can be | |
| played. | |
| Before calling systemInfo() with any of the other codes, you | |
| *must* check to see if systemInfo() itself is supported. Versions | |
| of the run-time prior to 2.2.4 do not support this function, so | |
| the return codes are meaningless. Fortunately, you can determine | |
| if systemInfo() is itself supported using the following code | |
| fragment: | |
| if (systemInfo(__SYSINFO_SYSINFO) = true) | |
| { | |
| /* | |
| * systemInfo IS supported by this run-time - other | |
| * systemInfo codes will return meaningful results | |
| */ | |
| } | |
| else | |
| { | |
| /* | |
| * systemInfo is NOT supported by this run-time | |
| */ | |
| } | |
| Only one version of HTML TADS (version 2.2.3) was ever released | |
| without systemInfo support, and this was the first public beta | |
| release. So, it should be fairly safe to assume that any system | |
| that doesn't support systemInfo() doesn't support any of the HTML | |
| TADS features. | |
| The __SYSINFO_xxx codes are: | |
| __SYSINFO_VERSION - returns a string with the run-time version | |
| number. This will be a string such as '2.2.4'. | |
| __SYSINFO_HTML - returns 1 if HTML markup is supported, 0 if not. | |
| 0 indicates that this is a standard text-mode run-time system. | |
| If this returns 0, then JPEG, PNG, WAV, MIDI, WAV/MIDI overlap, | |
| WAV overlap, and the images, sounds, music, and links preference | |
| items can all be assumed to be unsupported, since these features | |
| are only provided by HTML TADS. | |
| Note that __SYSINFO_HTML returns 0 (HTML not supported) for the | |
| character-mode run-time, even for the newer versions of the | |
| run-time that do provide some limited HTML support, because this | |
| information code is intended to indicate whether the full HTML | |
| feature set is supported. The character-mode version only supports | |
| a limited subset of HTML features, so it indicates that HTML is | |
| not supported. | |
| __SYSINFO_OS_NAME - returns a string with the name of the operating | |
| system on which the run-time is currently executing. (The name | |
| is the same as the string that the compiler uses to pre-define | |
| the __TADS_SYSTEM_NAME preprocessor symbol, but this lets you | |
| determine what system is executing at run-time, rather than the | |
| system that was used to compile the game.) | |
| __SYSINFO_JPEG - returns 1 if JPEG images are supported, 0 if not. | |
| __SYSINFO_PNG - returns 1 if PNG images are supported, 0 if not. | |
| __SYSINFO_WAV - returns 1 if WAV sounds are supported, 0 if not. | |
| __SYSINFO_MIDI - returns 1 if MIDI music is supported, 0 if not. | |
| __SYSINFO_MIDI_WAV_OVL - returns 1 if MIDI and WAV sounds can be | |
| played back simultaneously (overlapped), 0 if not. If this | |
| returns 0, it means that WAV playback will suspend MIDI playback. | |
| __SYSINFO_WAV_OVL - returns 1 if multiple WAV sounds can be played | |
| back simultaneously (overlapped), 0 if not. If this returns 0, | |
| it means that any WAV played back in a foreground layer will | |
| suspend a WAV being played in any background layer. | |
| __SYSINFO_PREF_IMAGES - returns 1 if the user preferences are set | |
| to allow images to be displayed, 0 if not. Note that, even if | |
| this preference is set so that images are not displayed, the | |
| preferences for JPEG and PNG images will still return 1 for each | |
| of those image types that are supported by the platform. The | |
| image format codes (__SYSINFO_PNF and __SYSINFO_JPEG) indicate | |
| whether the image formats are supported at all, whereas this | |
| preference code indicates whether images are currently allowed | |
| for display. | |
| __SYSINFO_PREF_SOUNDS - returns 1 if the user preferences are set | |
| to allow digitized sound effects (WAV files) to play back, 0 if | |
| not. | |
| __SYSINFO_PREF_MUSIC - returns 1 if the user preferences are set | |
| to allow music (MIDI files) to play back, 0 if not. | |
| __SYSINFO_PREF_LINKS - returns 0 if the user preferences are set | |
| so that links are not highlighted at all (in which case they'll | |
| be displayed as ordinary text; they won't be highlighted as links | |
| and won't be active for the mouse); returns 1 if links are | |
| highlighted and active; and returns 2 if links are set to a "hot | |
| key" mode, in which case links aren't highlighted except when | |
| the user is holding down a special key to explicitly illuminate | |
| the links. | |
| - TADS has a new mechanism for better supporting non-US character | |
| sets in a more portable fashion. The TADS Compiler, Run-Time, and | |
| Debugger (including the HTML TADS versions) now provide an option | |
| that lets you specify a character set translation to use for your | |
| game. The character set translation allows your game to use a | |
| standardized character set, such as ISO Latin 1, but still run on | |
| any system by providing a mapping from your game's internal character | |
| set to the native system character set for each player's system. | |
| Refer to CHARMAP.HTM for information on how to use this new | |
| feature. | |
| - The parser now accepts a comma immediately after "oops" or "o". | |
| - The parser now allows strings in player commands to be entered with | |
| single quotes as well as double quotes. So, the following commands | |
| are now interchangeable: | |
| type "hello" on keyboard | |
| type 'hello' on keyboard | |
| Note that this change means you can't create a vocabulary word that | |
| starts with a single quote, such as "'til", although you can still use | |
| a single quote within a vocabulary word, as in "bob's". The parser | |
| treats a single quote within a word as part of the word, but it now | |
| treats a single quote at the start of a word as a quotation mark | |
| intended to mark a string. | |
| - In adv.t, 'q' is now a synonym for 'quit' for player commands. | |
| - The #define preprocessor directive did not work correctly in past | |
| versions when the "-case-" option (for case-insensitive compilation) | |
| was used. In particular, preprocessor symbols defined with capital | |
| letters were not matched. This has been corrected so that #define | |
| works properly when "-case-" is used. | |
| - The #define preprocessor directive sometimes did not work correctly | |
| with long source lines (over about 128 characters). This has been | |
| corrected; #define now works correctly with long input lines, which | |
| are often necessary for lengthy macro expansions. | |
| - The TADS error code 1026, "wrong number of arguments to user function," | |
| now includes the name of the function or object.method that was the | |
| target of the invalid function call. This can be helpful in situations | |
| where the TADS parser calls the function directly, since there was | |
| previously no simple way of determining which function was being called | |
| in these cases. Note that you must compile your code with debugging | |
| enabled, and run under the TADS Debugger, to see the extra information | |
| in the error message, since only the Debugger can load a game's symbol | |
| table, and can only do so if the game was compiled for debugging. | |
| - A bug that caused debugger crashes under certain obscure circumstances | |
| has been fixed. In the past, if a game used the "replace" keyword to | |
| replace a method in an object, tracing into other methods in the same | |
| original object (the base object, before applying the modifications | |
| using the "modify" construct) could sometimes crash the debugger. | |
| This has been corrected. | |
| - The run-time is now more consistent about converting special escape | |
| sequences in strings obtained externally, such as from the input() | |
| function or from quoted strings in a player command. In particular, | |
| when these strings contain characters such as backslashes or newlines, | |
| the run-time now consistently converts these characters into a | |
| backslash sequence (a backslash turns into '\\', a newline turns | |
| into '\n', and a tab turns into '\t'). | |
| - adv.t and std.t are now considerably more consistent in terms of | |
| indenting and punctuation style. | |
| ------------------------------------------------------------------------------ | |
| 2.2.3 03/25/98 corrections and enhancements, HTML | |
| - This new version of the TADS compiler and run-time supports | |
| HTML TADS, the new HTML-enabled version of the TADS run-time. | |
| You should use this new version of the compiler to generate | |
| games for the HTML TADS run-time. | |
| - Fixed a parser bug that caused various problems when the player | |
| issued a command to an actor, but used a pronoun to refer to | |
| the actor: "him, go east." (This always failed, but the type | |
| of problem depended on the platform; in most cases, a TADS | |
| error such as "error loading object on demand" resulted.) | |
| - Fixed up the indenting in adv.t. At some point adv.t was | |
| detabified with an incorrect tab size setting, which randomized | |
| the indenting. The file has now been fully re-indented with | |
| spaces; since it has no tabs in it any more, it should look | |
| the same on any editor, regardless of the local tab size | |
| setting. | |
| - Added 'n', 's', and the other one-letter direction words to | |
| dirPrep's preposition vocabulary list in adv.t. This corrects | |
| the problem that a command such as "push box n" was not accepted, | |
| even though "push box north" was. | |
| - A new parser error handling function has been added. The new | |
| function, parseErrorParam(), is similar to parseError(), but | |
| provides additional arguments that provide the actual values of | |
| the "%" parameters in the error message: | |
| parseErrorParam: function(errNum, errMsg, ...) | |
| The errNum and errMsg parameters are the error code number and | |
| default error message text string, just as with parseError(). | |
| The additional arguments give the values of the "%" parameters; | |
| for example, for message 2, "I don't know the word '%s'", the | |
| first extra parameter will be a string with the word that the | |
| parser didn't recognize. | |
| If your game defines a parseErrorParam() function, the parser | |
| will call this function rather than parseError(); parseError() | |
| is essentially obsoleted by the new function. However, if your | |
| game does not contain a parseErrorParam() function, the parser | |
| will call parseError() as before. | |
| - Fixed a parser bug: if an object had the same name as a verb | |
| (for example, the defined an object with a noun of 'lock'), and | |
| the player attempted to issue a command to an actor starting | |
| with the re-used verb, the parser incorrectly issued the error | |
| "There's no verb in that sentence!" | |
| ------------------------------------------------------------------------------ | |
| Please consult TADSV222.DOS for information on releases from 2.1.1 | |
| through 2.2.2, and refer to TADSV200.DOS for information on releases | |
| prior to 2.1.1. See the note at the top of this file for details on | |
| where to find these older release notes files. | |
Xet Storage Details
- Size:
- 175 kB
- Xet hash:
- 4fc752434227dcbc508868325b8f08c3af0262296716c0bee68c15bc9300ffc7
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.