Recent Changes to the TADS 3 Library

This is a list of changes to adv3, the TADS 3 library. Changes are listed in reverse chronological order (the most recent changes are first).

3.0.18.1

Released 5/5/2009

A typo in the English message for refuseCommand has been fixed.
3.0.18

Released 4/28/2009

More of the lister classes have been reorganized along the same lines as the BaseContentsLister refactoring in version 3.0.17. In particular:
When an action remapping is inherited from a base class, a subclass or instance can now define a verify() method for the original action. In the past, only the new action's verify() was called - the remapping bypassed any dobjFor() or iobjFor() definitions for the old action. This made it impossible to tweak the subclass handling for verbs that were remapped in base classes.

With this change, the verify() for the new and old actions are both called, if there's a verify() for the old action defined on a subclass of the class where the remap() itself is defined. In other words, if the verify() "overrides" the remap(), the subclassed verify() is invoked.

Because the verify() methods for both the old and new actions are called, you can't actually override the new action's verify() call. But verify() results are cumulative, so you can use it to adjust the logicalness of the action, or to make the action illogical.

The parser can now has an option to generate more descriptive messages when noun phrases are disambiguated using the logicalness rules.

Disambiguation occurs when the player uses a noun phrase that matches more than one in-scope object. For example, if the current room contains a red door and a blue door, and the player types OPEN DOOR, the noun phrase DOOR could refer to either of the two door objects. The parser must decide which of the two objects the player is referring to; this process is called disambiguation. The parser does this by applying the various logical, logicalRank, illogicalNow, illogicalAlready, and related rules defined on the matching objects in the library and in your game code.

There are two kinds of disambiguation results: "clear" and "unclear". When exactly one of the objects involved is logical, it's a clear disambiguation result: we consider it safe to assume that the player was referring to that exact object, since the other choices simply don't make sense. When more than one object is logical, though, the parser can sometimes still pick one automatically (i.e., without generating an extra question asking the player to specify which one she intended) based on the "likelihood" rankings given by logicalRank rules. A pick based on likelihood rankings is an unclear result, because the player could plausibly have been referring to one of the other logical matches.

For unclear results, the parser has always generated a parenthetical announcement saying which object it chose. This is to alert the player to the parser's choice, so that any misunderstanding is immediately apparent.

However, in past versions, the parser didn't say anything to explicate clear disambiguation results. In practice, even clear results can sometimes disagree with the player's intentions, for the simple reason that the player sometimes misunderstands the current state of the objects. So some authors have found that it's better to make some kind of object announcement in every case, regardless of how clear the parser thinks the resolution is.

The parser now provides two new modes, in addition to the traditional mode, for announcing clear disambiguation results. These new modes are selected via the new gameMain property ambigAnnounceMode. You can set this in your gameMain object to select the behavior you prefer. The settings are:

Note that the new default behavior differs from the old behavior from before this feature was added. If you prefer the old behavior, you must explicitly set gameMain.ambigAnnounceMode to AnnounceUnclear.

The short-vs-long message selection for the DescribeClear option is done through a couple of new MessageHelper methods, shortTMsg() and shortTIMsg(). If you're adding new messages of your own, you can use these methods to do the same type of selection.

The parser can now accept short-hand replies to disambiguation queries involving a locational qualifier, using nothing more than an adjective or noun from one of the location names. For example:

<take torch
Which torch do you mean, the torch in the left sconce, or the torch in
the right sconce?

>left
Taken.

In the past, it was necessary for the response to be explicit about the location qualification: THE TORCH IN THE LEFT SCONCE, THE ONE IN THE LEFT, etc. In particular, the response had to actually use the locational phrasing to refer to the distinguishing location. This is no longer necessary; a player can now simply name one of the locations, and can even abbreviate to a single distinguishing word from the location name.

To support this change, the DisambigResolver now keeps track of the Distinguisher that was used to generate the question (the prompting message - "Which torch do you mean...?"). This lets the resolver look for qualifying phrases relevant to the distinguishing characteristic called out in the prompt.

The Distinguisher, meanwhile, has a couple of new methods that let it provide the added information: objInScope() lets the distinguisher add qualifying objects to the scope list, and matchName() lets the distinguisher recognize qualifying objects in addition to the original ambiguous objects. The locational and ownership distinguishers defined in the library provide suitable definitions for these new methods. Games that define their own custom Distinguisher subclasses might want to add these methods if they also make use of ancillary qualifier objects.

In the English library, PathPassage adjusts the logicalness of the verbs Enter and Go Through downward (to logical rank 50). These verbs aren't usually used with path-like objects in English; this logicalness adjustment tells the parser to pick other objects first in cases of ambiguity.
The new conversationManager method setRevealed(key) adds the given key to the revealed topic table. The library now calls this method any time it needs to add a key to the table (whereas it used to manipulate the table directly).

These changes don't affect any existing behavior - existing code will continue to work unchanged. The purpose of the changes is to provide a single point for overriding the default library behavior via 'modify'. In particular, you can override setRevealed() to store additional information about the revealed topic in its table entry, beyond the mere fact of the revelation. You could, for example, store the location where the key was revealed, or a time stamp marking when it was revealed.

A new ConvNode method, noteActiveReason(reason), extends the older noteActive() method by providing information on why the node is being activated. The 'reason' parameter is a string indicating what triggered the node change:

By default, this new method simply ignores the reason code and calls the noteActive() method. This ensures that existing code will work unchanged.

If you need the reason information when activating the node, override noteActiveReason(). Otherwise, you can override noteActive() as in the past.

The new reason code information is supplied when the caller changes the node by calling the new Actor method setConvNodeReason(). This is essentially an extended version of the existing setConvNode() method. The new method takes an additional argument giving the reason code, which it passes to noteActiveReason().

Note that the reason code will be nil if the node change is initiated by calling Actor.setConvNode() directly. All of the library calls to setConvNode() have been replaced by calls to setConvNodeReason(), so unless your game code is calling setConvNode() directly, the reason code will always be set properly. For compatibility with existing code, setConvNode() can still be called; it works as before, simply passing a nil reason code.

In the past, when an actor used conversational states (ConversationReadyState, InConversationState), terminating the conversation didn't take into account any ByeTopic objects in the current ConvNode within the conversation. This was because the conversation states have some special transition handling that overrides the usual code where the ConvNode ByeTopic would be found. The conversation states now take care to check the ConvNode in effect at the end of the conversation, so ByeTopic objects within a ConvNode should now interact properly with conversation states.
The transformation from "X, GIVE ME Y" to "ASK X FOR Y" that was introduced in 3.0.17 caused a run-time error if "Y" was a valid word but didn't resolve to an object. This has been corrected. (bugdb.tads.org #0000021)
In the past, when EXAMINE was used with ItemizingCollectiveGroup, the output omitted any object that wasn't listable through the room contents lister. This was a problem for objects such as Fixtures, which normally aren't displayed in a room listing.

ItemizingCollectiveGroup now calls a new method on self, examineUnlisted(x), to list each object x that isn't listed via the room contents lister. By default, this new method performs a nested EXAMINE command on the unlisted object to show its full description.

In Settable, the message routing properties have been changed slightly. In the past, the message properties okaySetToMsg and setToInvalidMsg were doubly redirected: okaySetToMsg had the value &okaySetToMsg, and setToInvalidMsg had the value &setToInvalidMsg. The TurnTo action handler that used these messages evaluated the properties directly, to get the property pointers, which the library then processed in the usual way.

The point of this arrangement was that subclasses of Settable (Dial in particular) could easily redirect these messages to different library messages, just by overriding the properties. However, this was inconsistent with the message scheme most other objects use, in that you couldn't override okaySetToMsg(val) in the Settable itself to yield the message text, which is how the message scheme works for most other objects.

The new version keeps the redirection step, but renames it to allow the standard override scheme to be used. The new property okaySetToMsgProp evaluates to &okaySetToMsg, and setToInvalidMsgProp evaluates to &setToInvalidMsg. This means that you can override okaySetToMsg(val) and setToInvalidMsg(val) using the standard pattern.

(bugdb.tads.org #0000045)

Underside and RearContainer now provide tryMovingObjInto() method definitions. The new methods try the obvious implied commands suitable for these objects: Underside tries an implied PutUnder, and RearContainer tries an implicit PutBehind.
The BannerWindow method updateForRestore() is now called during a Restore or Undo operation for every banner, even when the banner is already displayed. In the past, this method wasn't called on banner windows that were already present; it was only called when the window had to be created in the course of the Restore/Undo. It's now called uniformly for all banners.
3.0.17.1

Released 8/12/2008

This version has no library changes; this release is to correct a TADS 3 compiler problem introduced in 3.0.17.
3.0.17

Released 8/9/2008

Risk of incompatibility: The standard handling for the TakeFrom action has been changed slightly. The changes generally make it easier to code objects by unifying the handling of Take and TakeFrom for most objects, but some slight adjustments to existing code might be required - see below.

First, Thing.dobjFor(TakeFrom)'s action() handler now actually replaces the TakeFrom with a Take action. In the past, the handler merely invoked the Take action() handler. For the most part, this had the same effect as replacing the action, but not always; some arrangements of subclasses and overrides caused subtle differences. Actually replacing the action ensures that a successful TakeFrom is handled exactly like a regular Take, which means that custom behavior for Take will automatically apply exactly the same way to TakeFrom without any explicit TakeFrom overrides. Of course, you can still override dobjFor(TakeFrom) separately if you do want it to behave differently from Take.

Second, Immovable.dobjFor(Take) now fails the action in the check() handler rather than the action handler, which allows us to remove Immovable.dobjFor(TakeFrom) entirely. In the past, TakeFrom was separately overridden to ensure that the indirect object wouldn't attempt to carry out the action, but this is no longer necessary, since the basic Take check() phase (which TakeFrom's check() phase invokes) will fail the command.

These two changes should essentially eliminate the need to override TakeFrom when all you want to do is make it behave the same as Take. TakeFrom should now always follow Take's lead, so if you simply want TakeFrom and Take to behave the same way, just override Take and you're set.

Some existing code might not work properly with the changes to Immovable. In particular, you'll need to look at any objects of class Immovable (or any subclass of Immovable) where you've overridden the dobjFor(Take) action() handler. In the past, overriding the action() handler for Take was enough to override Immovable's handling of the command, since Immovable failed the command in the action() handler and did nothing in the check() handler. With the change, the check() handler cancels the action. This means that your overridden action() handler might never be invoked. The solution is simply to insert an empty dobjFor(Take) check() handler in your object:

   boulder: Immovable 'boulder' 'boulder' "It's a big boulder. "
      dobjFor(Take)
      {
        verify()
           { /* my original verify routine... */ }
        check() { }   // THIS HAD TO BE ADDED DUE TO THE LIBRARY CHANGE !!!
        action()
           { /* my original action routine... */ }
      }
    ;

If you're not sure whether you have any objects needing this change, you can add this bit of code to your game, and then run the game and type IMMTEST to get a list of objects that you should inspect:

   VerbRule(immtest) 'immtest' : IAction
     execAction()
     {
       "Immovables with dobjFor(Take) action() overrides:\n";
       forEachInstance(Immovable, new function(x)
       {
         if (overrides(x, Immovable, &actionDobjTake))
           "\t!!! <>\n";
       });
     }
   ;
Krister Fundin's Tips extension has now been integrated into the library as a standard feature. This system makes it easy to create custom one-time tips of the sort that the library has traditionally used for things like the explanation of the NOTIFY command the first time the game's score changes. The library now uses the Tips system for its standard tips, and the system is readily extensible with custom, game-specific tips. A new chapter in the Technical Manual describes the system.
It's now possible to determine if a script file is currently being read, and if so, the modes being used to read it. To get this information, call setScriptFile(ScriptReqGetStatus). If input is currently being read from a script, the function returns an integer value giving a combination of ScriptFileXxx flags describing the reading mode; for example, the return value will be (ScriptFileQuiet | ScriptFileNonstop) for a script being read in quiet, non-stop mode. Note that a return value of 0 (zero) indicates that a script is being read, but that none of the mode flags are applicable - that is, the script is being read in the default modes, with output and "more" prompts displayed. If a script is not currently being read, the return value is nil.

The new flag ScriptFileEvent will be included in the return value if applicable. This lets you determine whether the script being read is an event script or a plain command-line script. Note that this new flag is "query only" - if you include it in the 'flags' argument in a call to setScriptFile() to start reading a script, the function ignores it, since the script reader determines the type of the script to be read automatically by examining the file's contents. The purpose of this flag is to let you find out how the script reader is treating the file, rather than letting you tell the script reader how to treat the file.

Note that this new form of setScriptFile() is only supported in VM versions 3.0.17 and higher. If you call setScriptFile(ScriptReqGetStatus) on an earlier VM, a run-time error result, because older implementations of the function accept only a filename string or nil as the first argument. You can use try-catch to handle this situation - a run-time error indicates that this form of the function isn't supported, in which case the script status information is not available.

Two new actions have been added: RecordEvents and RecordEventsString. These are variations on the Record and RecordString actions that record event scripts (rather than ordinary command scripts, as Record and RecordString do). The English library defines verb syntax for these new actions ("record events", "record events on", and "record events 'filename'").
A new parsing mechanism makes it possible to rewrite commands based on "global" conditions. Unlike remapTo, which lets you associate remapping rules with the objects involved in the command, the new mechanism lets you rewrite a command even before the objects are resolved. There are cases where it's important to be able to apply the rewrite before any object resolution has taken place, because the resolution process itself can vary substantially depending on the type of action being performed. remapTo has to wait until after objects are resolved, which is sometimes too late.

This new mechanism is implemented via a new class, GlobalRemapping. To create a rewrite rule, you create a GlobalRemapping object, and define its getRemapping() method. This method looks at the action to determine if it's of interest, using whatever criteria you want. In most cases, this means checking the action type to see if it's an action you want to rewrite, and possibly checking to see if certain objects are involved as well.

The library defines one GlobalRemapping object, giveMeToAskFor, which rewrites commands of the form "X, GIVE ME Y" to the alternative format "ME, ASK X FOR Y". The library formerly tried to perform this same rewriting via an iobjFor(GiveTo) check() method in Actor, but this approach didn't work in many cases due to interference from several other checks made earlier in the execution process. The old check() scheme has been deleted in favor of the new mechanism.

For full details, see the new chapter on Global Command Remapping in the Technical Manual.

A new set of Action methods makes it possible to temporarily override the meaning of a pronoun, for as long as the action is in effect. Action.setPronounOverride() creates an override, which associates an object with a particular pronoun type (PronounMe, PronounYou, etc). This new meaning overrides the usual meaning, but only for the duration of the action. Action.getPronounOverride() looks for an override defined with setPronounOverride() for this action.

This new mechanism is designed mainly to support GlobalRemapping. In particular, a remapping that changes the target actor usually needs to override the meaning of "you" in the rewritten action, so that "you", "your", and "yours" are interpreted as referring to the original target actor rather than the target actor of the rewritten format.

For details, see the new chapter "Global Command Remapping" in the Technical Manual.

The classes BaseSurfaceContentsLister, BaseRearContentsLister, and BaseUndersideContentsLister are now all based on a new class, BaseContentsLister. This new class unifies the code that was formerly defined separately in the three subclasses - BaseSurfaceContentsLister, BaseRearContentsLister, and BaseUndersideContentsLister are now trivial subclasses with no overrides of their own. (They're still present as separate classes, (1) for the sake of compatibility with existing code, and (2) because the distinct classes could still be useful for other types of container-type-specific overrides beyond what the library defines.)

The three subclasses exist to provide customized wording in the contents listings generated for the corresponding specialized container types. However, the variation in the wording provided by the library definitions was always very simple - the only thing that varies is the preposition used to describe the relationship between the container and its contents ("on" for a Surface, "under" for an Underside object, "behind" for a RearContainer or RearSurface). As it turns out, the needed preposition is available as a property of the container object, namely objInPrep. This makes it possible to unify the code for the three classes by referring to this property rather than hard-coding the preposition - by doing this, the code for the three subclasses becomes identical, which allows it to be consolidated in a common base class.

The advantage of this change is that it's no longer necessary to change a specialized container's contents-lister class simply because you want to change the preposition used to describe the relationship between contents and container. In the past, you had to change both objInPrep and the contents lister. Now, changing objInPrep is enough - since the default contents listers refer to objInPrep, changes to objInPrep will automatically flow through to the wording in contents listings.

Because the class structure is the same as before, existing code should work unchanged, and you can still use contents lister customizations to achieve effects beyond the simple wording changes that objInPrep provides, if needed.

The new class ActorHelloTopic makes it easier to create a greeting message for cases where the NPC initiates a conversation through a script. Place an ActorHelloTopic object anywhere a HelloTopic would go to create a greeting specifically for the case of an NPC-initiated conversation.

[Update for 3.0.18: disregard the following. These changes were not effected as described, and weren't quite what was intended. See this entry for an update.]

ByeTopic objects can now be used with actors who use conversational states (ConversationReadyState, InConversationState). In the past, the conversational states largely bypassed the topic-based Goodbye processing, so goodbyes had to be written with the specialized goodbye methods on the state objects. This made the conversation states harder to use, since it created special cases you had to be aware of. Now, you can set up a goodbye message by locating a ByeTopic within the InConversationState or within any ConvNode.

The class ComplexComponent now defines stagingLocations as the lexical parent's (which is normally the ComplexContainer's) location. This makes it easier to embed a nested room (a platform, booth, etc) as a component of a complex container object, by allowing inbound travel into the nested room to bypass the complex container and move directly between the enclosing location and the component/nested room. As with other aspects of complex containers, the container and component are meant to look and act like a single object, from the player's perspective, so it's not desirable to treat the container and component as separate objects for the purposes of nested room travel.

Along the same lines, ComplexContainer now tries to remap the actions StandOn, SitOn, LieOn, Board, and Enter to a suitable component. These actions are now automatically remapped to the object returned from the new method getNestedRoomDest(), if any. If that method returns nil, the actions are not remapped after all.

The new getNestedRoomDest() method of ComplexContainer looks for a suitable component to use as the destination of a nested room travel command. By default, it first looks to see if the sub-surface component is a NestedRoom object, and if so, returns that; otherwise, it checks the sub-container component, and returns that if that's a NestedRoom; otherwise it returns nil. This default behavior makes everything automatic for cases where you have exactly one component that's a nested room. If you want to define more than one nested room within a complex container (for example, a large crate that you can STAND ON and also GET IN), you will need to override getNestedRoomDest() to return the appropriate destination by verb, or you can simply override the dobjFor(action) remapTo() definition to point remap to the correct component.

A bug in the parser sometimes caused "truncated plurals" to be selected ahead of exact matches in a player's answer to a disambiguation question. By design, the parser prefers an exact match to a singular noun ahead of a partial match to a plural (for example, with the default six-letter truncation, the word "object" in command input would match either the singular "object" or the plural "objects", but the parser assume the singular word was intended because it's an exact match for the input). However, this principle was being ignored in this case due to the bug. This has now been corrected. (bugdb.tads.org #0000006)
In the past, if a ConvNode was activated via the <.convnode> tag, and the ConvNode's noteActive() method displayed any text, the text displayed within noteActive() appeared out of order in the final output relative to the text containing the <.convnode> tag. This happened because <.convnode> is processed within an output filter, so the entire string containing the <.convnode> had to be processed by the filter before any of it was actually displayed; if any text was displayed in the course of recursively called routines such as noteActive(), that text would as a result show up first in the final output.

The <.convnode> output filter now captures any text that's displayed in the course of activating a new ConvNode, and re-inserts the text into the stream at the proper point. This ensures that the text appears in the correct sequence, so it's now safe to display text from within a noteActive() routine. (bugdb.tads.org #0000003)

In the past, TIAction verbs (those taking a direct object and an indirect object, such as PUT IN or UNLOCK WITH) had the side effect of clearing the parser's memory of gender-specific pronoun antecedents. For example, after typing "LOOK AT BOB; UNLOCK THE DOOR WITH HIS KEY", the parser would forget that "him" refers to Bob, even though there wasn't anyone else mentioned in the second command to supersede the meaning of "him". This has been fixed. (bugdb.tads.org #0000020)
The Room class now applies a lower level of disambiguation "likelihood" for the Examine action to a remote room than for a room containing the actor. In the past, the class applied a lower-than-default likelihood for a room containing the actor, on the assumption that a player who types EXAMINE FOO probably means to examine a nearby object rather than the enclosing room, when both are called FOO. However, there's a third possibility, which is that there's a remote room (i.e., a separate top-level room that's connected to the current location by a sense connection) in scope that's also called FOO. In these cases, we'd still want to select the nearby object over either room; but in cases where we only have the two rooms to decide between, it seems most likely that the player would want to refer to the enclosing room rather than the remote one. This new distinction in the likelihood levels of enclosing and remote rooms accomplishes this: the most likely selection is still a nearby non-room object, the next most likely is the enclosing room, and the least likely is a remote room.
3.0.16

Released 4/10/2008

The new class SimpleLister provides simplified interfaces for creating formatted lists of items. This class is useful when you just want to create a textual listing, using the standard serial-comma generation and so on, without all the hassle of specifying a sense environment, point of view, etc. The class also has a method that creates a text string for a listing (rather than displaying the list directly, which is what the base Lister normally does).

Two concrete instances of SimpleLister are provided, to further simplify particular listings: objectLister, which can be used to format a list of simulation objects; and stringLister, which can be used to format a list of string values.

Commands of the form "X, Y", where both X and Y were words not in the game's dictionary, caused a run-time error ("nil object reference"). This was due to a library bug in the code that resolves the actor object when giving orders to an actor ("BOB, GO EAST"). This has been corrected.
The Goal object has a new property, goalFullyDisplayed, that the hint system automatically sets to true upon displaying the last hint in the Goal's menu list. This can be used, for example, to close a hint for a red herring after the player's seen the full list of hints; this might be desirable to remove clutter from the menu, since the player probably won't worry about an object again once it's been revealed that it's just a red herring.
The templates for TravelMessage, NoTravelMessage, and DeadEndConnector now accept an event list (given as a list in square brackets) in lieu of a double-quoted message string. These classes were already mixable (via multiple inheritance) with EventList, so this change simply makes it more convenient to define instances when using this capability.
Top-level rooms (i.e., Room objects) didn't properly handle ENTER commands (e.g., ENTER HALLWAY) when attempted from a separate top-level room connected via a sense connector. The ENTER handling for Room incorrectly assumed that only one top-level room was in scope at a time, which isn't true when rooms have sense connections. The handler also failed to handle the simpler situation where the actor was in a nested room within the target room.

The Room dobjFor(Enter) handler has been improved to handle such situations. The new handling is as follows:

In the past, when NestedRoom.checkMovingActorInto() decided it had to perform an implied action to move the actor into the nested room, the routine assumed that the action failed if the actor didn't end up in the nested room and in the room's default posture. This condition does hold for the library NestedRoom subclasses when the defaults are inherited, but overriding either the default posture or the implied command for a particular instance can make the condition fail. When the condition fails, the routine infers that the implied action failed, and terminates the whole command.

This condition - specifically, the test for posture - is overly strict. The routine isn't documented as guaranteeing the default posture, and in fact it never did guarantee the default posture anyway, as the implied action was always bypassed if the actor's location was initially correct, regardless of posture.

So, the condition has now been changed to remove the default posture check. The implied command (if any) is now considered successful if the actor is directly in the nested room afterward.

Two changes to BasicChair simplify the way the "get in" commands for the object and its subclasses are implemented.

First, the tryMovingIntoNested() routines in BasicChair and its subclasses have been unified into a single base-class method. In the past, each BasicChair subclass overrode tryMovingIntoNested() to perform the implied action matching the subclass type - SitOn for BasicChair, LieOn for BasicBed, StandOn for BasicPlatform.

Now, the overrides have been removed, and the functionality has been been unified into a single method on BasicChair that the subclasses all inherit. The BasicChair version of the method handles all of the subclasses by deferring to the default posture object (as returned from the defaultPosture property).

Second, the dobjFor(Board) definitions have been unified in a single BasicChair definition, rather than requiring overrides in each subclass as in the past. As with tryMovingIntoNested(), the dobjFor(Board) definition in BasicChair now calls upon the default posture object to carry out the appropriate command. This removes the need to override dobjFor(Board) in each subclass.

The benefit of these changes is that there's less to override when creating a custom type of BasicChair subclass. Setting the defaultPosture also automatically takes care of setting the implied action for "get in" and the mapped action for "board", since tryMovingIntoNested() and dobjFor(Board) now choose the sub-action that matches the default posture. Of course, if for some reason you want these actions to differ from what the default posture selects, you can still override tryMovingIntoNested() and/or dobjFor(Board) to achieve whatever special effects you want. For typical objects where the two match, though, these changes means less work is needed to keep everything in sync.

The "standing" posture object's setActorToPosture() method has been changed slightly. In the past, the routine performed an intransitive Stand action, which causes the actor to stand up in whatever location it's currently occupying. This was at odds with the specification of the method: the routine should not only make the actor stand up, but make the actor stand in a given location; the old version ignored the target location. The routine now does use the target location, and executes a transitive StandOn action with the target location as direct object.

The old implementation was the way it was because the transitive StandOn action didn't always work properly when applied to top-level rooms. However, the handling of StandOn for top-level rooms has since been improved, so this is now a viable implementation. This makes the method work consistently with its specification and with the corresponding methods in the other pre-defined posture objects.

The Decoration class now overrides the SEARCH action so that it uses the standard Thing handling, to be consistent with the way LOOK IN is handled. In the past, SEARCH instead was handled by the Decoration catch-all verb handler, which shows the generic "that's not important" message. This led to different default Decoration behavior for SEARCH vs LOOK IN, which was inconsistent with the library's usual default treatment of the two actions as synonymous.
The English library's typographical output filter object (typographicalOutputFilter) ordinarily adds an "en" space (a space that's slightly wider than a normal inter-word space) after any sentence-ending punctuation, such as a period or question mark. This produces looser spacing between sentences, which some people find easier to read. However, it had an undesirable side effect, which was to add the same spacing after honorific abbreviations like Mr. and Mrs. - these look exactly like sentence endings, as far as the simple substitution rule was concerned.

The filter now specifically looks for a pre-defined set of abbreviations, and suppresses the extra spacing when it finds one. The abbreviations are given by the property typographicalOutputFilter.abbreviations - to change or add to the set of abbreviations, modify typographicalOutputFilter to change this property. (Note that this property is only evaluated once, at program start-up, so changing it on the fly won't have any effect. If you do want to modify the set of abbreviations dynamically, you'll have to instead update typographicalOutputFilter.abbrevPat to use a new RexPattern object with the new set of abbreviations.)

DistanceConnector now allows moving objects through the connector via moveInto - in particular, DistanceConnector now overrides checkMoveThrough() to return a "success" indication. In the past, DistanceConnector inherited checkMoveThrough() from SenseConnector, which disallowed a move via moveInto() if the distance connector was involved.

In the abstract, there's usually no good reason that an object can't be moved through a distance connector. The only reason to disallow it might be that the distance involved is so great that it would take an impractical amount of time to complete the move.

More to the point, though, player commands will generally disallow this kind of operation for reasons of reachability - a touchObj precondition is involved for any basic command that moves an object from one container to another (TAKE, DROP, PUT IN, PUT ON, etc), and that precondition will fail separately because the distance object is unreachable. Because of this, a moveInto() involving a distance connector is almost certainly due to a programmatic (scripted) operation, rather than a basic player command. In the past, you could work around this by calling moveIntoForTravel() instead of moveInto(), but this was a needless complication. This change allows moveInto() to succeed even if a distance connector is involved.

The ShuffledEventList object has a new property, suppressRepeats, that lets you control whether or not a given event can occur twice in a row. This property corresponds to the suppressRepeats property of the ShuffledList object.

By default, this is set to true, which yields the former behavior. You can set this to nil if you don't want to suppress repeats. With very small lists (of three or four elements), the suppression of repeats sometimes makes the result sequence look fairly un-random, because the no-repeats constraint reduces the number of available permutations. If you see output for a small event list that doesn't look random enough, you might try setting suppressRepeats to nil to see if that produces more pleasing results.

ShuffledList now ignores suppressRepeats for a two-element list. If the list has only two elements, and repeats aren't allowed, the list will necessarily yield the predictable sequence A-B-A-B..., which defeats the purpose of the shuffled list.
The Lockable class's dobjFor(Open) check() method now inherits the default handling. Since Lockable is designed to be used as a mix-in class, this addition ensures that any additional Open checks inherited from other superclasses are properly invoked.
The comment pre-parser object (commentPreParser) now sets a runOrder value that's lower than the default, so that it runs before other pre-parsers that inherit the default runOrder. In most cases, it's unnecessary (and even potentially problematic) to run comments through other pre-parsers, since most are designed to parse the usual sorts of command syntax, not the free-form text allowed in comments. Running the comment pre-parser first effectively bypasses other pre-parsers when a comment is encountered, since the comment pre-parser halts further processing of the command when it detects that the command is actually a comment.
In the past, if an actor returned nil from obeyCommand(), the parser ignored the command for the purposes of the AGAIN command. A subsequent AGAIN command in this case simply repeated the previous command - the one just before the refused order. This has been corrected; the parser now remembers a command for AGAIN even if the actor to whom the command is directed refuses it.
Thing.setGlobalParamName() didn't correctly delete any previous name from the global table. (This would only have been a problem if you used the method more than once on a particular object, to change the object's global parameter name on the fly, which seems highly unlikely.) This has been corrected.
3.0.15.3

Released 11/2/2007

There are no library changes in this release.
3.0.15.2

Released 9/13/2007

NonObviousVerifyResult.resultRank is now defined as the same value as IllogicalVerifyResult.resultRank. This makes the parser treat illogical and non-obvious objects as equivalent in cases of ambiguity, so that the parser won't choose one over the other. In the past, non-obvious objects were ranked lower than illogical objects, which caused the parser to choose an illogical object over a non-obvious object in cases of ambiguity. This didn't quite make sense; for the purposes of choosing among ambiguous objects, non-obvious objects really should be treated as equivalent to ordinarily illogical objects, since the point of the non-obvious designation is that the object appears at first glance to be illogical for a given verb.
The parser now incorporates Michel Nizette's vocabLikelihood extension. This extension lets each object set an "intrinsic likelihood" value that affects how the parser chooses among ambiguous objects. When the parser can't tell the difference between two objects using the normal 'verify' procedure, it compares the vocabLikelihood properties of the two objects, and chooses the one with the higher value if they differ. This makes it easier to fine-tune the disambiguation process to reduce unnecessary prompting.
SensoryEmanation.endEmanation() now resets displayCount to nil. displayCount was documented as being automatically reset when a period of continuous presence ended, but in the past no actual resetting was done. We set the value to nil, rather than to 0 or 1, to make it more obvious (when debugging, for example) that the object is not currently in scope.
The English library now defines suitable custom objInPrep, actorInPrep, and actorOutOfPrep values for Surface, Underside, and RearContainer. This ensures that the default messages produce reasonable relational descriptions when referring to the contents of these specialized container types. (In the past, only Surface.objInPrep was suitably customized.)
BasicOpenable.tryImplicitRemoveObstructor() now only attempts an implied Open if the object is closed. When the object is already open, the routine now does nothing. This change is necessary because objects can conceivably create an obstruction even if they're already open: once the object's open, if it's still creating an obstruction, another implied Open would be pointless (and, in fact, could cause an infinite loop).
The new Thing method isOrIsIn(obj) returns true if 'self' is either inside 'obj' or equal to 'obj'. This is useful for cases where you want to check if a given object is anywhere within another object's containment tree, including the other object itself.
When a MultiLoc object is the point-of-view object in a sense calculation, it now connects all of its multiple containers to the scope in addDirectConnections(). In the past, a MultiLoc's containers were only considered part of the scope when the point of view was inside the MultiLoc, but it makes sense for the MultiLoc itself to be the same way, since it inherently "sees" all of its containers.
LocateInParent is now defined as a class. (It was always meant to be, but the definition lacked an explicit 'class' keyword in past version.)
3.0.15.1

Released 7/19/2007

Compatibility warning: The message property cannotTalkToSelf has been renamed to cannotTalkToSelfMsg, for consistency with other message property names. (You should search your existing game code and update any references to the old name to use the new name instead.)

The new gameMain property beforeRunsBeforeCheck lets you control the relative ordering of the "check" phase and "before" notifiers.

In the past, the library always ran the "before" notifiers - beforeAction and roomBeforeAction - before running the check() handlers for the objects involved in the action. To ensure compatibility, the default setting for the new property is beforeRunsBeforeCheck = true - this causes "before" to run before check(), as it always has.

However, in many ways, it's more logical and practical to run the check() phase first first, and then run the "before" notifiers. This ordering allows the "before" handlers to assume that the action is more or less committed to running to completion, since they know that the check() phase has already tested the action and allowed it to proceed. The most common use of "before" handlers is to carry out side effects of an action, so by running all of the check() tests first, the "before" handlers can more confidently invoke their side effects, without worrying that the action will later fail due to a check() condition.

Now, you can never truly call an action "committed" until it's been fully carried out, since even a "before" handler can conceivably cancel a command (with "exit", for instance). You could set up a situation where an action is affected by two "before" handlers, and the one that runs later cancels the command - thus invalidating any assumption in the first one that the command is committed. However, this is relatively unusual, and in any case it's under your control: if you don't use "before" handlers to cancel commands, it won't occur.

If you want to use the new alternative ordering, where "check" runs before the "before" notifications, simply set beforeRunsBeforeCheck to nil in your gameMain object. Some authors have tested this ordering with existing games and found it to work well with existing code, but be aware that you could trigger subtle changes - you might have unknowingly created dependencies on the relative ordering of check() and "before" that will only show up under certain conditions. We therefore recommend using the new alternative ordering only for new code or for projects that are relatively early in their development cycle, to ensure thorough testing of the new ordering.

ThingState now allows tokens to be shared among multiple states associated with the same object. In the past, state words had to be unique among an object's states: it wasn't possible to have a set of states such as "unpainted", "painted red", and "painted blue", because it was invalid for "painted" to appear in two states. This sort of sharing is now allowed.

The old rule was that a word could match an object only if the word didn't appear in any of the object's other (i.e., non-current) ThingState token lists. The new rule is that a word can match an object if (a) the word appears in the object's current ThingState token list, or (b) the word doesn't appear in any of the object's other ThingState token lists. Condition (a) is what's new; it allows words to be shared among states by allowing a word to match the current state as long as it's in the current state's list, regardless of whether it appears in other states' lists.

In the English library, when a command triggers a chain of several implied actions, the announcement for the implied actions now adds the word "then" in each conjunction: "(first standing up, then taking the box, then opening it)". In the past, "then" appeared only in the final conjunction; most people find it reads more naturally to use "then" in each conjunction.
The English library now uses the adjustDefaultObjectPrep() mechanism to generate implied action announcements. This means that verbs like SitOn and GetOutOf will use object-specific prepositions when they're announced as implied actions: "(first sitting on the bench)" vs "(first sitting in the car)".
The actions GetOutOf and GetOffOf now adjust the prepositions in their generated verb phrases according to the actorOutOfPrep definition for the direct object.
AccompanyingInTravelState.initiateTopic() now defers to the next state object. Since the in-travel state is designed to be ephemeral, it's unlikely that one of these would actually contain its own initiated topics, so in almost all cases the desired topic would come from the state that the actor will be in following the travel.
The library now defines a simple template for ShuffledList. The new template takes a single list parameter giving the value list.
ListGroupCustom now defines groupDisplaysSublist = nil by default. This type of group is intended mainly for cases where you want to display a collective message to describe the group, rather than listing its elements individually, so we usually don't want the inclusion of a ListGroupCustom message to trigger the "long list" format (i.e., with semicolons) in the overall list. This change makes this behvior the default.
Non-physical CollectiveGroup objects (i.e., those with 'nil' locations) didn't work when the associated individuals were MultiLoc or SenseConnector objects. This has been corrected.
Actor.knowAbout() now considers an object to be known if it's currently in sight. In the past, an object was known if it was specifically marked as known, or it was marked as having been previously seen. For the most part, an object that's currently visible will also be marked as having been previously seen, since this marking occurs whenever a Look command or travel to a new location lists an object. However, if an object comes into scope (via moveInto) while an actor is in a given location, the object won't be marked as having been seen until the next Look command or subsequent travel away from and then back to the location.

This change corrects a subtle problem that involved possessive phrases in commands. In the past, an object that just came into scope was excluded from matching a possessive noun phrase under certain circumstances, such as in the response to a disambiguation question. This should no longer occur.

In the past, the library seemed to allow commands like PUSH TV INTO BOX (i.e., commands to push a pushable object into a nested room), but on closer inspection it didn't really carry them out: instead, it carried out only the underlying basic travel command, without actually moving the object supposedly being pushed around. The library now rejects PUSH INTO nested room commands with a new message, &cannotPushObjectNested ("You can't push the TV there").

The library rejects these commands by default because we think it'll be relatively rare that games will need to allow them. Given that, it didn't seem worth complicating things by adding a bunch of new methods to provide generalized support, and we also didn't want to introduce that much new code at the current point in the release cycle.

However, the library does now provide a new hook that will make it easier for games to implement PUSH INTO nested commands if they need to. The new hook will also make it possible to implement riding vehicles into nested rooms, which wasn't previously possible. The new hook is a new Traveler method, travelerTravelWithin().

This new method is called on the Traveler object when a nested-room travel command (GET IN, STAND ON, SIT ON, LIE ON, PUSH X INTO, etc) is performed. In simple cases, the Traveler is simply the Actor performing command. However, if the actor is riding in a Vehicle, the Traveler is the Vehicle object; and if the command is a PUSH travel command, such as PUSH TV INTO BOX, the Traveler is a special ephemeral object, of class PushTraveler, created just for the command.

The library provides default implementations of the new hook method for Actor, Vehicles, and PushTraveler. For Actor, the method simply does the same thing that travelWithin() used to do, so existing code will work as it did before. For Vehicle, the method calls the same method on the Actor, so again it will work as it used to. For PushTraveler, the method simply terminates the command with the new message &cannotPushObjectNested.

If you want to allow pushing objects into nested rooms, you'll need to modify PushTraveler.travelerTravelWithin() to do basically the same thing that PushTraveler.travelerTravelTo() does. You'll probably want to set up a new set of notification methods parallel to beforeMovePushable() and movePushable() - you could just use those same routines, but you'll probably want to set up new custom nested-room versions instead, since the conditions and messages will probably need to be different for nested rooms from those used for room-to-room travel.

Note that you could also take advantage of the new method to allow an actor to ride a vehicle into a nested room. The default handling in Vehicle defers to the underlying Actor, and the actor performs the travel as though the Vehicle were any other nested room - which means that the actor gets out of the vehicle, then gets into the new nested room. If you instead wanted this type of command to mean "ride vehicle into nested room," you can override travelerTravelWithin() on the vehicle, so that the method moves the Vehicle itself into the new nested room. (You'll also have to set up preconditions that move the vehicle to the proper staging locations using the same rules that are used for actors, which could take a little work - that's the main reason the library doesn't support it out of the box yet.)

In the past, if an NPC was using AccompanyingState to follow around the player character, and the pair went through an AutoClosingDoor, the door was reported as having closed twice during the turn - once for each actor going through. AutoClosingDoor now skips the auto-closing step if the actor going through is in an accompanying state.
The parser's "OOPS" mechanism had a problematic interaction with literal phrases that showed up under certain conditions. In particular, if the player entered a command that caused some kind of follow-up question, such as a disambiguation query ("which book do you mean...") or missing-object prompt ("what do you want to unlock it with?"), and the player chose to ignore the follow-up question and instead just enter a new command, and the new command contained a literal phrase ("type asdf on keypad", say), the OOPS mechanism was overly aggressive in deciding the command contained a typo.

The root of the problem was that follow-up questions are parsed according to their own special grammars, which are much more limited than the full grammar used at the main command prompt. Normally, when the player responds with something that doesn't match one of these special grammars, the parser assumes the player intended to bypass the follow-up question and just enter a new command. However, if parser manages to find a non-dictionary word in the player's input, it invokes the OOPS mechanism to see if the player actually intended to answer the follow-up question but made a typographical error entering the response. The problem is that if the player entered a literal-phrase verb, the presence of a non-dictionary word doesn't necessarily imply a typographical error. For normal commands (as opposed to follow-up questions), this isn't an issue, because the parser finds the literal when matching against the full grammar. For follow-up questions, though, the parser never noticed the literal-phrase interpretation of the input, since it wasn't matching against the full grammar, so it prematurely decided that the input as mis-typed.

The OOPS mechanism now makes an extra check when presented with a non-dictionary word in the response to a follow-up question. Before deciding that the input really does contain a typo, the OOPS mechanism first checks the input to see if it matches anything in the full command-line grammar; if the parser finds a match, it assumes that the entry was meant as a brand new command, and so it bypasses the follow-up question and re-parses the input as a new command.

In the past, calling getOrigTokenList() on an EventAction object caused a run-time error. This was because EventActions don't have the token list data that the parser normally attaches when creating an Action to represent a parsed command line, which in turn is because these actions aren't created from command parsing but are instead "synthetic" actions, created internally by the library for bookkeeping purposes. (This same problem might have affected other synthetic actions as well, but it's only known to have shown up with EventActions.)

This has been corrected. Action.getOrigTokenList() now checks to make sure there's a parser token list attached to the action, and if not, the method return the parent action's token list; and if there's no parent action, the method simply returns an empty list.

HermitActorState now sets its property limitSuggestsion to true by default, to prevent the TOPICS command from displaying any topic suggestions from the actor or conversation node. This is desirable because the hermit state effectively blocks any topics defined outside the state: the whole point of the state is that it makes the actor essentially unresponsive to conversational overtures. So there's generally no point in offering topic suggestions while the actor is in the state.

In some cases, you might want to override this for a particular hermit state object. In particular, if it's not outwardly obvious that the actor will be unresponsive, you might still want to allow suggestions, since the player has no way of knowing that the actor won't respond to questions until she attempts asking them. Similarly, if the hermit state is expected to persist for a short time only, you might want to continue to allow suggestions so that the player knows there are useful topics to explore when the actor becomes responsive again.

ByeTopic now overrides impliesGreeting to suppress any implied HELLO greeting. This means that if the player explicitly says GOODBYE to an NPC, and no conversation is in progress, the goodbye response will be displayed by itself, without an implied greeting exchange. In the past, ByeTopic inherited the default 'true' value for impliesGreeting, so saying GOODBYE to an NPC outside of a conversation caused an implied greeting, followed immediately by the explicit goodbye response. In most cases, this ended up looking pretty strange - not only was the self-canceling HELLO-GOODBYE exchange odd on its face, but in most cases the implied greeting also looked redundant from the player's perspective, because a player tended to say GOODBYE explicitly only when she was under the impression that some kind of conversational interaction was already under way.
In the past, conversation-flow problems sometimes occurred if a topic changed its "isConversational" status as a side effect of showing the response for the topic. This was because ActorTopicDatabase.showTopicResponse() waited until after invoking the response to determine whether the response should affect the conversation flow, so the status change side effect caused showTopicResponse() to use the new setting - meant for the next response - rather than the setting for the response it actually showed. This has been corrected; showTopicResponse() now makes its determination based on the isConversational status as it stands just before invoking the response.
The finishGameMsg() function now explicitly runs the score notifier daemon before it displays the end-of-game message. This ensures that the usual score-change message is displayed if any points were awarded in the course of the action that triggered the end of the game. Without the explicit invocation, the notifier daemon wouldn't typically get a chance to show a notification for the final point award: the notification is normally shown just before the next command prompt after points are awarded, and at the end of the game there's not usually another command prompt forthcoming.
In the English library, inlineListingContentsLister didn't always generate correct verb agreement for its prefix message for a "wide" listing ("You are carrying tongs (which contains ...)"). This has been corrected.
In the past, searching a RoomPart nominally containing a RoomPartItem sometimes caused a "nil object reference" run-time error. This was due to a missing point-of-view object setting; the code where the error occurred now applies a default POV of the command actor when no explicit POV is set.
Container and Openable objects now apply a touchObj precondition to the Search action. This requires that the actor be able to reach the object in order to search it. This change is intended to better enforce the idea that searching a container involves some actual physical manipulation, such as shifting the contents around to look behind or under things, momentarily removing contents to see what's underneath, and so on. The actor at least has to be able to reach the object to perform this kind of manipulation.
In the past, AGAIN didn't work properly when used to repeat a FOLLOW command involving an NPC using the "guided tour" mechanism (the TourGuide and GuidedTourState classes). This was due to a subtle turn-timer problem with AGAIN that affected commands like FOLLOW that use nested or replacement actions in the course of their execution. This problem was observed in the FOLLOW/AGAIN situation, but could also have affected other commands. The root problem has been corrected.
3.0.15

Released 3/8/2007

The Follow action has always adjusted the scope list to include any NPCs that the target actor remembers having seen leave, whether or not they're still within sight. This allows you to FOLLOW a character who's no longer present - the whole point of FOLLOW would be defeated if this weren't possible, after all. However, in the past, this scope list expansion created duplicate entries in the scope list if an actor with a "follow memory" was also still physically present - the actor was added to the scope list once for ordinary visibility, then again for the "follow memory." This duplication caused problems in some situations, since other parts of the library expect the scope list to contain only unique entries. The Follow action now ensures that the list remains unique after its additions.
3.0.14

Released 2/9/2007

There are no library changes in TADS 3.0.14.

3.0.13

Released 1/19/2007

The adv3 English library has a new option setting for the parser's truncation length. The truncation length has always been customizable in the low-level system objects that the parser uses to look up and match vocabulary words, but the library didn't formerly provide a good way for games to override its truncation length setting. In effect, the truncation length was hard-coded into the English library, and the only way for authors to change it was to edit en_us.t; most authors would rather not do that because of the hassle of merging their own changes into future library updates.

The new setting is in gameMain.parserTruncLength. The default setting is 6 characters - this is the same as the English parser's old hard-wired setting, so existing code will behave the same as it did in the past.

As part of this change, the new method languageGlobals.setStringComparator() lets you change the active string comparator dynamically. You can use this method if you want to change the truncation length, or other comprator settings, on the fly during the game.

The English library now allows a TAction VerbRule to specify a custom preposition for a default object announcement, overriding the preposition coded in the verbPhrase. This is useful for a few verbs where the preposition tends to vary according to the direct object. For example, SIT might use ON in some cases but IN for other cases: "on the stool" vs "in the car."

To accomplish this, TAction.announceDefaultObject() first parses the verbPhrase to get the standard preposition, then calls a new method on self, adjustDefaultObjectPrep(prep, obj). 'prep' is the default preposition from the verbPhrase, and 'obj' is the direct object of the command. The routine returns the new preposition string. The default implementation simply returns 'prep', but the StandOn, SitOn, and LieOn actions override this to return the direct object's actorInPrep.

InitiateTopics can now be located within ActorState objects. In the past, InitiateTopics were only found when they were directly within an Actor or a TopicGroup within an actor. The library now looks for InitiateTopics in the current actor state object as well, which is more consistent with other topic types.
inputManager.pauseForMore() now re-activates the transcript if it was active before the pause. In the past, this function simply flushed output and left the transcript deactivated. This was problematic for certain library subsystems that depend upon the transcript being active throughout a command. Now, the function flushes the transcript's pending output up to the pause, then reactivates the transcript after the pause, so that transcript capture proceeds as normal throughout the rest of the command.
In the past, the library generally assumed that NestedRoom objects would only be located inside either Rooms or other NestedRooms, but never inside ordinary Things. However, in practice it's often useful to be able to put a nested room inside something that's not itself a nested room.

A number of small library changes should now make NestedRooms work properly when located inside ordinary Thing objects. The changes are mostly internal and should have no impact on existing code, but for reference, here's what's changed:

In addition to the changes above, NestedRoom no longer assumes that its immediate location is the destination when an actor within the nested room leaves the nested room. Instead, NestedRoom now consistently uses the value given by exitDestination. Further, NestedRoom.exitDestination itself no longer uses the immediate container as its default setting, but instead uses the default staging location as given by the defaultStagingLocation method.

A change in 3.0.10 introduced a parsing problem with distinguishing nouns from plurals when the words were right at the parser's truncation length. To be more precise, if you defined a noun that was exactly the truncation length, and you also defined a plural for that noun as the noun plus "s" (or, actually, as the noun plus any other letters), then the parser would incorrectly interpret the noun word in player input as though it were plural.

For example, the default truncation length is 6 characters, so this problem occurred with "button" and "buttons" defined as a noun and plural, respectively. In this case, if you had two or more objects in scope with vocabulary like 'red button*buttons', and the player typed something like "x button", the parser incorrectly treated the "button" in the player's input as though it were plural, so it applied the Examine command iteratively to all of the buttons in scope, rather than asking the player which singular button they intended to examine.

This problem has now been corrected.

The message definition for cannotSetToMsg was missing from the English message list (in en_us/msg_neu.t). The message is now there.
A command of the form PUSH pushable INTO object, where the second object was some random, non-enterable thing, yielded an unformatted message ("{that/he dobj} {is} not something you can enter"). The underlying problem was that the action remapping invoked by PushTravelViaIobjAction didn't properly set up the object parameters for the remapped verb, leaving the message system unable to find the direct object. This has been corrected.
The Attachable class had a problem that caused a run-time error when the player entered a command of the form DETACH object, where object was an Attachable and the command had no indirect object. The problem came from a name conflict between an Attachable method called cannotDetachMsg(obj), which took one parameter, and a library messages property of the same name, which takes no parameters. When the message resolution system tried to retrieve the library message from the direct object, it invoked the zero-parameter version of the property, which caused the run-time error due to the parameter mismatch with the one-parameter version implemented in Attachable.

To fix this problem, the method in Attachable has been renamed to cannotDetachMsgFor(obj). The library message has the same name as before.

Any existing code that overrode the Attachable method will have to adjust for the name change. That said, it seems almost impossible for this change to affect existing code, since the very bug that we're talking about here would have prevented the override from working properly in the first place.

A library bug caused a run-time error ("stack overflow") on commands like REMOVE ME or REMOVE nested room containing me. This has been corrected.

(The specific problem is as follows. The library assumes that a REMOVE X command will result in the actor taking X, and so as a sanity check calculates how much weight the actor would be holding if that were allowed. When X is the actor or contains the actor, the hypothetical weight check created a circular containment situation; the stack overflow came from the library's attempt to recursively visit all of the hypothetical contents of the actor, which is of course an infinite loop in a circular containment situation. To avoid this problem, the library now ignores hypotheticals that would create circular containment. It's safe to ignore these hypothetical tests because commands that perform them should always be disallowed anyway, since actual circular containment is never allowed.)

A library bug caused a run-time error ("nil object reference") on entering a command of the form object, unknown word, where object was any in-scope non-Actor object. This has been corrected.

(The problem came about because the parser attempted to treat such a command as though it were directed to an NPC. This is some special handling that applies when the command has a syntax error; the point is to let the author customize the parsing error messages for orders given to particular actors. However, when object isn't an actor, this is problematic, because the library assumes that it can call certain Actor methods on the object in question. The fix is that the library will only apply this handling in cases where the object is actually an Actor; in other cases, it won't assume that the command was intended as an order to an NPC, so it will simply use the default parsing error messages.)

3.0.12

TADS 3.0 General Release version - Released 9/15/2006

The library didn't properly handle situations where an NPC order involved multiple objects and a failed implied sub-action. If an implied action failed for one of the objects involved, the implied action was assumed to have failed for all subsequent objects in the multiple object list as well, even if the subsequent implied actions actually succeeded. This resulted in self-contradictory transcripts, where an implied action was reported as successful, but then was followed by a message that the implied action had failed:

  Bob takes the coin.
  Bob must be holding the coin first.

The problem was that the implied action mechanism was incorrectly considering the failure status for the entire top-level command when determining if the subsequent implied actions failed. Instead, it should have been checking the status of the implied actions themselves. It now does this by looking for a failure report within the implied action's reports, rather than looking for a failure anywhere within the entire transcript.

SpaceOverlay.getWeight() now omits the object's "contents" from the weight calculation if the contents are to be abandoned when the object is moved. If the contents are to be abandoned, it means that they're not actually attached to or contained within the space overlay, but are simply colocated with it; they thus have no contribution to the overlay's total weight. If the contents are not to be abandoned on moving the overlay object, they're effectively attached to it, so they do contribute to its weight as normal.
CaptureFilter is now a subclass of OutputFilter (it was formerly just an 'object'); and SwitchableCaptureFilter is a subclass of CaptureFilter (it also was just an 'object'). This should make no difference functionally, as an output filter is only required to implement the filterText() method, but is desirable anyway in that it makes an ofKind(OutputFilter) test recognize these object types as output filter subclasses.
3.0.11

Released 9/8/2006

Very slight compatibility risk: NameAsOther (and thus NameAsParent) no longer maps the "in" names to its target object. The "in" names are the names generated when an object is described as contained within the NameAsOther. In the past, these were mapped to the target object along with all of the ordinary names for the object. However, this was the wrong behavior for ComplexComponent, which inherits from NameAsOther, because the containment relationship between a ComplexContainer and its contents is defined by the container subclass mixed with ComplexComponent in the object's superclass list, not by the target object, which in this case is the ComplexContainer of which the ComplexComponent is a part.

Although it's conceivable that some other applications of NameAsOther would actually want the old behavior, it seems highly unlikely, so we don't expect any practical compatibility list.

However, we have provided a new mix-in class, ChildNameAsOther, that adds mappings for all of the "in" names to the target object. So, if you have a NameAsOther that depends upon the old "in" name mapping, just add ChildNameAsOther to the object's superclass list (right after NameAsOther or NameAsParent), and you'll get the same behavior as before.

In 3.0.10, the ImpByeTopic was differentiated into a couple of subclasses to allow handling implicit goodbyes differently when desired (see below). However, this change didn't handle one type of goodbye correctly, namely the NPC-initiated goodbye, via npc.endConversation(). The change incorrectly made it so that those goodbyes were treated the same as explicit player-initiated goodbyes.

Prior to the 3.0.10 change, NPC-initiated goodbyes were subsumed into the undifferentiated "implicit goodbye" category, and they should clearly remain in that category; they simply need to be differentiated like the other implicit goodbyes were in the 3.0.10 change.

To this end, the new topic class ActorByeTopic has been added; this is analogous to BoredByeTopic and LeaveByeTopic, and is used when the NPC terminated the conversation via npc.endConversation(). In the absence of an active ActorByeTopic, the active ImpByeTopic will be used instead. This restores compatibility with pre-3.0.10 code (where there was no differentiation among "implied goodbye" types, and npc.endConversation() events were handled as implied goodbyes), while providing a specific topic type to handle this one type of goodbyes.

The English library's "instructions" module (instruct.t) now uses HTML markups to display typographical quotes and apostrophes ("curly quotes") throughout the text of the standard instructions. Thanks to Greg Boettcher for making this improvement.
The detailed-naming scheme for object announcements (see below) introduced in 3.0.10 has been made optional, and disabled by default. Testing reveals that the mechanism as currently designed is too twitchy for some people's taste, so we've disabled it by default for the time being; however, the code is all intact, for those who want to use it as-is or tweak it for their needs. To enable the detailed announcement naming, set gameMain.useDistinguishersInAnnouncements to true.
The BannerWindow system had a flaw in the way it re-initialized banners after a RESTART. The problem showed up in cases where there were dependency orderings among the windows, so that one window's initBannerWindow() had to call another's initBannerWindow() in order to create the windows in the correct order. The problem didn't always happen even in cases of ordering dependencies, since it also depended on the arbitrary ordering of the VM's internal object lists. When it happened, the problem manifested itself by creating extra copies of an affected banner window at each RESTART. This has now been corrected.
In banner.t, the formerly anonymous InitObject that handles banner initialization (and post-RESTART re-initialization) now has a name, bannerInit. This is so that games can use "modify" and "replace" with the object, and also so they can refer to it from the execBeforeMe properties of other InitObjects, for initialization dependency ordering purposes.
"Follow mode" for NPCs ("Bob, follow me") didn't work correctly when the actor being followed moved between two top-level rooms connected by a sight sense connector. This has been corrected. (The problem was that the code that carried out the "follow" attempted to move the follower using a "local travel" action - something like STAND ON STAGE or ENTER BOOTH - any time the target actor was still in sight. The follower now attempts local travel only if the target is still in sight and the target is within the same top-level room; otherwise, the follow uses a suitable full-fledged travel action.)
In Actor.actorActionFollow(), if the actor is already in "follow" mode for the requested other actor, a message is now displayed to this effect (alreadyFollowModeMsg: "I'm already following Bob"). This won't be a factor by default, since the library automatically cancels "follow" mode any time a new command is issued to an actor before attempting to enact the new command. However, games might want to override this auto-cancel behavior, in which case they might encounter this situation in actorActionFollow(). In the past, the routine did nothing at all - it didn't even show a message, so the generally undesirable "Nothing happens" was displayed by default.
In the past, an actor that started in an InConversationState triggered a run-time error at start-up, due to an initialization order problem in the library. This has been corrected. (In particular, an actor's boredAgendaItem property is now initialized via a perInstance() definition rather than in initializeActor(). This ensures that the property is initialized before it's needed. In the past, the order of initializations sometimes resulted in the library trying to use the actor's BoredAgendaItem object before it was initialized, leading to a "nil object reference" error.)
Room's condition for remapping the GetOutOf action to the Out action has changed slightly. In the past, this remapping was performed only if the 'out' direction had an apparent destination, which is only the case when the actor attempting the travel already knows the destination (such as from past experience or at-hand information). This condition wasn't quite right, though. Instead, the condition should have been simply that the 'out' direction has an apparent connector - that is, there's a visible way to travel in the 'out' direction. Room has been changed to use the new condition.
The template (in en_us/en_us.h) for DeadEndConnector now makes the apprentDestName entry optional. This allows using the template to define a dead-end connector that merely displays a message when traversed, without giving it a name.
In the English library, PathPassage now limits its remapping of the Take action to the TravelVia action to cases where the entered verb phrase was literally "take." This prevents other phrasings, such as "pick up path" or "get path," from being interpreted as attempts to travel along a path.
The message sayTravelingRemotely in the English library has been corrected to add the word "to" before the destination name.
3.0.10

Released 8/17/2006

Incompatibility warning: (This note concerns a change that was made in 3.0.9 but inadvertantly omitted from the 3.0.9 release notes. We're mentioning it now in case anyone was affected by it and needs help adjusting their code for the change.)

In version 3.0.9, gameMain.verboseMode was changed from a simple true/nil property to an object of class BinarySettingsItem. Any existing game code that attempted to turn verbose mode on or off by setting gameMain.verboseMode to true or nil will now encounter a run-time error the first time the player enters a travel command.

If you want to set the default verbosity mode explicitly in your game, you can't do it any more by setting gameMain.verboseMode to true or nil. Instead, you can add a line like this to a start-up routine, such as gameMain.newGame():

   gameMain.verboseMode.isOn = true; // turn on verbose mode

Note that the verbosity mode is now part of the "global preferences" mechanism, so in most cases it's best for games not to change it explicitly, instead leaving it up to the player to decide on the setting. In the past, some authors liked to set a verbosity mode that they felt was most suitable for the game. Now that the player can specify a set of default preferences that they wish to apply to all games, it's better for authors not to presume to change the player's default settings without a good reason. As with any other rule, there are bound to be exceptions, so if you have a really good reason to override the player's preferences then you should feel free to do so. But if you're tempted to override the player's preferences just because you like it a particular way, you might want to reconsider.

Minor incompatibility warning: The Lockable class now has initiallyLocked set to true. This means that all Lockable objects now start out locked by default (i.e., unless your game code specifically overrides initiallyLocked to set it to nil for a given Lockable). In the past, Lockable didn't define initiallyLocked at all (so it defaulted to nil), but Door and IndirectLockable defined it as true - so some Lockables formerly started out locked by default while others were unlocked by default. This change should be generally beneficial, since (1) it simplifies matters by making the initial lock status consistent across all Lockables, and (2) the vast majority of Lockables start out locked anyway, so "locked" is the better default.

If you have any Lockables in existing code that you specifically intended to start out unlocked, and you didn't explicitly set initiallyLocked to nil in those objects, you'll have to do so now.

Minor incompatibility warning: The method Action.callVerifyPrecond has been renamed to callVerifyPreCond - that is, the 'c' in 'precond' is now capitalized. This change is for better naming consistency, since all of the other symbol names in the library that include the substring "precond" capitalize the C. This routine is intended mostly for internal use within the library, so it should affect little or no existing game code.
Minor incompatibility warning: The NOTE command has been removed, and replaced by a new comment syntax that lets the player enter a comment by starting a command line with a punctuation-mark prefix.

The default comment prefix is now an asterisk ("*"). You can change this to any prefix string you'd like by modifying the commentPrefix property of the commentPreParser object. You can also control whether or not leading whitespace is allowed before the comment prefix (it is by default) by modifying commentPreParser's leadPat property.

Using NOTE as the comment prefix was problematic because NOTE is a common enough word that games often want to use it in an object's name, and this creates situations where a user might want to start an input line with NOTE with the intention of referring to an object, not of entering a comment. It was essentially impossible in some of these cases to reliably determine which the player meant. The new approach avoids these problems by using syntax that should be unambiguous in nearly all games.

In the past, there was a StringPreParser object in the English library that helped the NOTE command by quoting the note text in some cases. This preparser has been removed, and a new preparser has been added in its place, but this time in the general library, in misc.t. The new object is called commentPreParser, and it performs all of the comment handling itself, without the need for a separate NOTE action.

As part of this change, NoteAction and NoteIAction have been eliminated. In the unlikely event that you used 'modify' to change the behavior of these actions, you'll have to rework your code. Look at the commentPreParser object for details of the new arrangement.

Minor incompatibility warning: Each actor now has its own separate lookup table of ConvNode names. This means that you don't have to worry about making ConvNode names unique globally - you only have to make sure that names are unique within a single actor's set of nodes.

This shouldn't affect existing game code, except for cases where you refer directly to conversationManager.convNodeTab. You should scan your source for occurrences of "convNodeTab" and change any that you find to "actor.convNodeTab", where "actor" is the Actor who owns the table. The most likely place for game code to refer to convNodeTab is in a "modify conversationManager" definition.

Minor incompatibility warning: Thing.getExtraScopeItems() is now required to return a list - nil is no longer a valid return value for this method. Use an empty list instead of nil to indicate that there's nothing to add to the current scope. The old nil return code was inconsistent and unnecessary, as an empty list always functionally meant the same thing anyway.

It's very unlikely that this change will affect any existing game code, since any game code that overrides this method almost certainly did so to add something to the scope. However, you do a quick search through your game code for getExtraScopeItems(), and make sure that you don't have any nil returns; if you do, simply change them to empty lists ("return [];").

Minor incompatibility warning: Thing.getListedContentsInExamine() has been renamed to getContentsForExamine(), and its operation changed slightly. The old name was a bit misleading, in that the method actually needs to return all of the visible contents of the object, whether or not they're marked as listable. Unlistable contents need to be included in this list so that their listable contents can be included recursively. This is the operational change: in the past, only listable contents were included in the returned list; now, all visible contents are included.

This change corrects an inconsistency that occurred in cases where an object had fixed-in-place contents that themselves had contents. In the past, directly examining such an object didn't mention anything about the contents of the second-level containers, while a simple LOOK did include the inner contents. The change makes the two cases consistent.

This change should have little or no effect on existing code, since the former getListedContentsInExamine() method is an internal method that's unlikely to have been called or overridden in game code. If you overrode this routine, though, you'll need to apply the name change to your code, and make any adjustments for the slight change in the method's semantics.

Minor incompatibility warning: the "equivalent object" mechanism has changed substantially, although existing game code shouldn't be affected unless it was modifying the equivalence mechanism itself.

In the past, equivalence was based on the superclass of the object. Two objects were equivalent if (a) they both had isEquivalent set to true, and (b) they had identical superclass lists. This approach to equivalence was occasionally problematic, particularly when using "proxy" objects to modify the behavior of objects involved in equivalence groups. In addition, the superclass approach was somewhat counterintuitive: the whole point of the equivalence mechanism is to treat objects with identical names as interchangeable, but using superclasses as the basis of the equivalence had nothing to do with the naming.

The new scheme is based on a new property called equivalenceKey, which is defined in the language module. In the English library, the default setting of this new property is the disambigName of the object. So, equivalence groups are now simply based on the basic disambiguation name of the object. This is much more consistent with the disambiguation mechanism itself, because it means that the parser decides whether objects are interchangeable using essentially the same logic that the player uses intuitively: if the game always refers to two objects by the same name, they're interchangeable.

Another advantage of the new scheme is that it's much more customizable than the old scheme. Since the basis of the equivalence decision is now distinguished as a separate property (equivalenceKey), you can control equivalence groups simply by overriding the property. You could even effectively restore the old scheme by defining equivalenceKey as getSuperclassList() - this would make the immediate superclass list of an object the basis of its equivalence grouping, producing the same behavior as in the past.

Note that if you change an object's equivalenceKey dynamically during play - or if you change its underlying disambigName property - the object's listing group won't be automatically updated. If you do make such a change and you want to update the object's listing group, just call initializeEquivalent() on the object. (Under the old scheme, there was really no way to change an object's grouping - even if you changed its name, it was still grouped based on its class, which could have caused strange results in listings. The new scheme at least allows for this kind of change, although it requires this manual step to keep the list grouping settings in sync.)

In the past, when EXAMINE was applied to a Room, the Room simply turned the command into a nested LOOK AROUND command. This has changed slightly. Now, the Room directly does the work that LOOK AROUND would do, without the nested command, and with the difference that the initial line with the room name in boldface isn't included in the output. (This is accomplished by omitting the LookRoomName flag from the 'verbose' flags for the Actor.lookAround() call.)

There are two reasons for this change. First, when the player explicitly types EXAMINE room name, it's somewhat redundant to include the room name in the output. Second, when the player types EXAMINE ALL or EXAMINE list of things, the standard multi-object-command output format already prefaces the results for each item in the list with the name of the item ("kitchen:"), which made the room name line especially redundant in this case.

An object can now be associated with more than one CollectiveGroup. This allows an object to be a member of multiple disjoint groups. For example, you could create a group for "treasure," and a group for "jewelry," and put some objects in both groups, while leaving others in one group or the other.

To define multiple CollectiveGroups for a given object, use the new property 'collectiveGroups' on the objects that you want to associate with the groups. Set this property to a list with the CollectiveGroup objects to associate with the given object.

You can still use the single-valued 'collectiveGroup' property, so existing game code will continue to work unchanged. However, 'collectiveGroup' is now obsolescent, so you should not use it for new code - use 'collectiveGroups' instead. For compatibility with existing code, the library's default definition of Thing.collectiveGroups is a method that returns an empty list if collectiveGroup is nil, or a one-element list containing the collectiveGroup value if the value is not nil. Since this is slightly ineffecient, support for the old 'collectiveGroup' will probably eventually be removed, at which point the default for 'collectiveGroups' will simply be an empty list.

Several new classes bring functionality parallel to RestrictedContainer to surfaces, undersides, and rear containers and surfaces. The new classes are RestrictedSurface, RestrictedUnderside, RestrictedRearContainer, and RestrictedRearSurface. These new classes (along with RestrictedContainer itself) are all based on the new mix-in class RestrictedHolder, which defines generic containment restriction rules that can be applied to any container subtype.

The new classes work just like RestrictedContainer did, and RestrictedContainer itself hasn't changed its behavior (it's been refactored slightly for the new base class, but this is just an internal change that shouldn't affect any existing code). To accommodate the new types of restriction, suitable new library "player action" messages have been added (cannotPutOnRestrictedMsg, cannotPutUnderRestrictedMsg, cannotPutBehindRestrictedMsg).

In the past, the default Search action handling was the same as for LookIn. Now, the Search handling is slightly different for Container and Openable.

First, Openable adds an objOpen precondition for Search if the actor isn't inside the object; for LookIn, this is skipped if the object is transparent.

Second, in the Search check() method, Container requires that the object be openable or transparent to the "touch" sense, or that the actor is inside the container. LookIn is similar, but only requires that the container to be non-opaque in the "sight" sense.

The reason for these changes is that Search implies a more thorough, physical examination than LookIn does. To most people, an explicit search involves physically poking through an object's contents, while "look in" is more passive, implying just having a look inside.

In the past, the Thing handlers for PUT UNDER and PUT BEHIND included touchObj preconditions on the direct object instead of objHeld preconditions. These have been changed to objHeld conditions; since these verbs generally require physically moving the direct object, the correct precondition is that the direct object be held, just as for the other PUT verbs.
A new Action method, getEnteredVerbPhrase(), returns the action's original verb phrase text as typed by the player. The return value is a string giving the verb phrase in a canonical format: specifically, the result is all in lower-case, and each noun phrase in the player's input is replaced by a placeholder token: '(dobj)' for the direct object, '(iobj)' for the indirect object, '(topic)' for a topic phrase, and '(literal)' for a literal text phrase. Only the verb phrase is included in the result - there's no target actor phrase, and no sentence-ending punctuation included. The noun phrase replacements apply to the entire noun phrases, so the single placeholder '(dobj)' could represent an entire list of direct objects. For example, if the player types "BOB, PUT THE BOOK AND PENCIL IN THE BOX AND GIVE IT TO ME", calling getEnteredVerbPhrase() on the PutIn action would yield 'put (dobj) in (iobj)', and calling the method on the GiveTo would yield 'give (dobj) to (iobj)'.

There are two reasons why the method returns the canonical format rather than the full text of the entire command. First, the full text is already readily available, via gAction.getOrigText(), so there's no need for a new method to retrieve this information. Second, and more importantly, the canonical format isoaltes the verb phrase structure of the player's input, independently of any noun phrases, making it easy to determine exactly which verb phrasing the player actually used.

This method is most useful in cases where the library's verb rules define two or more different phrasings for the same Action, and you need to be able to distinguish exactly which variant the player entered. For the most part, this is unnecessary: when the library's verb rules include synonyms, it's because the different phrasings usually have exactly the same abstract meaning, hence it's enough to know which Action matched the grammar. In some cases, though, a particular verb applied to a particular object has an idiomatic meaning different from the usual meaning for other objects, and in those cases the generic synonyms often fail to be idiomatic synonyms.

For example, the library defines "get (dobj)" and "take (dobj)" as synonyms for the Take action, because GET and TAKE can almost always be used interchangeably. However, if you were defining an "aspirin" object, you might want to treat the command "take aspirin" as meaning "eat aspirin," but you would still want "get aspirin" to mean "pick up aspirin." You could handle this by overriding the dobjFor(Take) action() method on the aspirin object, comparing gAction.getEnteredVerbPhrase() to 'take (dobj)', and calling replaceAction(Eat, self) if it's a match.

The new Action method getPredicate() returns the verb phrase match tree object that the parser resolved to the Action. The English library uses Action objects as predicate match tree objects, so in the English version this simply yields the original Action. However, non-English libraries can use separate objects for verb phrase match tree objects, so if you need any information from the verb phrase's grammar tree (for example, if you need to call getOrigText() or getOrigTokenList()), you should use action.getPredicate() rather than using the action object directly. (This does not apply to the new method getEnteredVerbPhrase() above - that method is explicitly defined on the Action, not the grammar object. getEnteredVerbPhrase() itself calls getPredicate() to get the grammar information, so you can simply call getEnteredVerbPhrase() directly on the Action object.)
The library looks to a new global variable, gLibMessages, to determine which object to use as the "library message" source. (This is the object used for messages that don't logically relate to the current actor; generally, these messages are replies to meta-commands, or text fragments used to construct descriptions.)

In the past, the library simply used the libMessages object directly as the source of these messages. Now, the library instead uses the current value of gLibMessages as the message source. The default value of gLibMessages is libMessages, and the library never changes this value itself, so the default behavior is exactly the same as before.

The purpose of this change is to allow a game to switch to a new set of library messages dynamically during play. For example, if your game is structured into chapters with different points of view, you might want to use a distinctive writing styles for the different chapters, in which case you'd want to change all of the library messages at each chapter transition. To do this, simply set gLibMessages to refer to a new object at each point when you want to switch message sources; subsequent messages will come from your new source object. Note that it's not necessary to do this if you only want to customize messages statically (i.e., throughout the entire game); for that, you can just 'modify libMessages'.

In the past, the Floor class replied to THROW object AT FLOOR with the same default message ("you should just put it down instead") in all cases. This reply is no longer used in cases where the Floor is unreachable; instead, the standard THROW handling applies, since the player couldn't accomplish the same thing with DROP.
RoomPart.getHitFallDestination() will now take into account any explicit 'location' setting in the RoomPart when determining the destination. In the past, an explicit 'location' setting was ignored, and a valid destination could only be found if the RoomPart was in the same top-level room as the actor. Now, if the RoomPart has an explicit non-nil location setting, that takes precedence in determining the destination. This is useful when creating connected top-level locations with room parts that you want capable of serving as THROW targets.
When throwing an object at a target, and the target happens also to be the place where the object is described as landing when thrown at that target (i.e., the target is the nominal drop destination of its own the "hit-fall" destination), a new default message is used, of the form "The projectile lands on the target." In the past, the message was the awkward "The projectile hits the target without any obvious effect, and falls to the target." This was most likely to occur when the target was something like a floor or ground object, since that's the only case where the target is likely to also be the nominal drop destination. This change is in the routine DropTypeThrow.standardReport().
GIVE TO, SHOW TO, and THROW TO now use different messages depending on whether the indirect object is an actor or an inanimate object. For ordinary Things, the message is now of the form "You can't give anything to X"; for Actors, the message is "The X does not appear interested." In the past, the latter message was used for all objects, animate or not. Imputing "interest" to an inanimate object could be read as either intentionally snide (as though the library were sarcastically calling the player dense for trying such an obviously stupid thing) or simply wrong (as though the library didn't know the difference); since msg_neu is supposed to affect a neutral narrative tone, neither alternative is desirable.

In addition, showing or giving an Actor to itself is now handled with a separate message ("Showing X to itself would accomplishing nothing").

REMOVE dobj now uses a separate reply if the object is already being held: "There's nothing to remove the X from." In the past, the command was unconditionally turned into TAKE dobj, which showed "(from you)" as the implied indirect object part, which was a bit awkward.
When the THROW direction command is used with a nonportable object (as in THROW DESK EAST), the result is now the same as for commands like MOVE and PUSH: "The desk cannot be moved."
The default response for THROW DOWN has been changed to use the same message as for DROP when applied to an object that's not currently being held: "You're not carrying that."
In statusLine.beginStatusLine(), in the StatusModeApi case, there was some leftover old code ("<body bgcolor=statusbg text=statustext>") right after the call to setColorScheme() that effectively undid the color settings made in setColorScheme(). The old code has been deleted.
The library now uses the same default message in response to a command to throw the player character in a direction (as in THROW ME EAST) as it does as for THROW ME AT something.
A new Thing method, adjustThrowDestination(), gives a prospective landing site for a thrown object a chance to redirect the landing to another object. getHitFallDestination() now treats the result it calculates using getDropDestination() as tentative, and calls this new method on the tentative result object to determine the actual destination. The default implementation of the new method in Thing simply returns 'self', which confirms the tentative destination as the actual destination. BulkLimiter overrides the new method to redirect the landing site to the BulkLimiter's location's drop destination if the thrown object would overflow the BulkLimiter's capacity.

This change corrects a problem that occurred when a thrown object landed in a BulkLimiter that was already near capacity. In the past, the BulkLimiter applied its capacity control by effectively blocking the THROW before it happened. With the change, the THROW will be allowed to proceed, and the thrown object will land in the nearest suitable container of the original target.

Note that any game code that overrides getHitFallDestination() should be sure to call adjustThrowDestination() on its tentative return value. Games usually won't have any reason to override getHitFallDestination(), so this change shouldn't affect most existing code.

In ActorState, the beforeTravel() method no longer ends the current conversation if the actor is merely moving between nested locations within the same top-level location. These aren't usually true departures that should trigger conversation termination, so the method no longer treats them as such.
A new travel connector class, DeadEndConnector, can be used for situations where travel through the connector is impossible, but for reasons that can only be learned by attempting to go through the connector. For example, this can be used for a passage that turns out to be blocked by a cave-in that isn't visible from the passage entrance, or in a situation where you describe the actor as wandering around in a direction for a while but giving up and returning to the point of origin.

DeadEndConnector supplements FakeConnector, which is most useful for exits that look like connections but can't be traversed for reasons that are apparent before the travel is ever physically attempted - in particular, for motivational reasons ("You can't leave town without finding your missing brother"). DeadEndConnector differs from FakeConnector in that DeadEndConnector acts as though the physical travel were actually occurring. It fires all of the normal travel notifications, so any side effects that would occur on ordinary travel will also occur with a DeadEndConnector.

"Push travel" has been changed slightly to handle situations where the object being pushed has side effects that affect the description of the destination location. The most common situation where this arises is when the object being pushed is (or contains) a light source, but it could happen in many other custom-coded situations.

In the past, the object being pushed wasn't moved to its new location until after the new location had been described. The point of this sequencing was to exclude the pushed object from the new location's description, simply by keeping it out of the new location until after the description was displayed. This exclusion is desirable because it would otherwise look as though the pushed object were already in the new location on the player character's arrival, which would be confusing. However, it prevented any side effects of the pushed object's presence from being taken into account in the new location's description. If the pushed object was a light source, for example, and the new location was otherwise unlit, the new location was described as dark.

Now, the library tentatively moves the pushable object to the destination of the travel, and marks the object (via the new Actor.excludeFromLookAround mechanism) for exclusion from the location description. It then moves the actor and shows the description of the new location. Finally, it moves the pushable back to the origin location, and then moves a second time, this time for real, to the destination location.

(The reason for moving the pushable twice - first tentatively, before the travel, then again "for real," after the travel - is that the method that makes the final move is overridable and so might actually do something other than move the pushable to the travel destination. Even if it leaves the object unmoved, though, or moves it to a different final destination, the tentative first move is still a valid intermediate step. At the point we're generating the description of the new room, the player character is in the process of pushing the pushable into the new room - the PC is notionally walking along with the pushable at that stage. If the overridable final-move method wants to do something different, it can do so; it will simply have to describe the change, which it had to do in the past anyway. At that point, the PC will already be in the new location, and so will in fact have pushed the pushable this far; anything that happens in the overridable method happens after the intermediate stage where we generated the description, so any side effects of the pushable's tentative presence were valid at that point, no matter what happens afterwards.)

In the English library, the Push-Travel commands (PUSH X NORTH, PUSH X UP RAMP, etc) now only accept single direct objects. In the past, the grammar phrasings allowed direct object lists, but actually pushing multiple objects into a new room is a practical impossibility, so there's no point in accepting it in the grammar.
The mechanism for describing "local" NPC travel has been enhanced. Local NPC travel is travel where an NPC moves from one top-level location to another, and both top-level locations are in view of the PC. In these cases, special handling is needed because the NPC isn't truly arriving or departing in the usual sense; the NPC is instead moving closer to, further away from, or laterally with respect to, the PC.

In the past, the local NPC travel mechanism did everything via the "local arrival" message: the NPC generated only this one special message at the destination end of the travel. The new mechanism adds two messages analogous to the local arrival message: a "local departure" message and a "remote travel" message. Here's how they're used:

These new rules obviously require some new methods. Here's the new arrangement of methods:

  • Existing code should continue to work correctly with the new framework, although with one caveat: if you customized a describeLocalArrival() or sayArrivingLocally() method, you might want to add corresponding customizations for the new local-departure and/or remote-travel methods. If you don't, your local-arrival customization will still work in the cases where it applies under the new rules, but it won't be invoked in all the cases it was in the past.
  • AccompanyingInTravelState now describes local travel using its standard departure message. This ensures that the correct messages are displayed when accompanying an actor in local travel or travel between two connected top-level locations (such as locations linked by distance).
    TravelAction.actionOfKind(cls) now properly handles the case where 'cls' is the base TravelAction class. (In the past, asking about TravelAction itself caused a run-time error.)
    The various SettingsItem objects defined in the library are now all named objects. In the past, some of these (such as gameMain.verboseMode) were defined as embedded objects; this made it more difficult to modify their behavior, since it wasn't possible to use 'modify' to alter the objects directly. The behavior should be exactly the same as in the past; the only difference is that the objects are now easier to customize.
    The putDestMessage that was defined in defaultFloor and defaultGround has been moved to the base Floor class instead - this message should be common to most floor/ground type objects, not just for the default ones in the library.
    The method standing.tryMakingPosture(loc) now generates the command STAND ON loc, rather than simply STAND as it did in the past. This makes the behavior consistent with the sitting and lying postures.
    Room can now be used as the direct object of STAND ON, SIT ON, and LIE ON. These are handled simply by remapping the commands to the room's "floor" object (specifically, the object returned from the room's roomFloor method). This is in part to accommodate the change above to standing.tryMakingPosture(), and in part to provide better automatic handling for player commands like SIT IN YARD.
    RoomPart now applies some additional disambiguation filtering in cases where two or more top-level locations are linked by a sense connector. When a resolve list contains multiple RoomPart objects, and some of those RoomParts are local and some remote, RoomPart will reduce the list to include only the local RoomParts. (A "local" object is one within the same top-level location as the actor; a "remote" object is one that's in a separate top-level location that's linked by a sense connector.)
    If a RoomPart has an explicit 'location' setting, that location will no longer add the RoomPart redundantly to the location's 'contents' list. In the past, the RoomPart would show up twice in the contents list, because it was added once by virtue of being in the RoomParts list, and again by virtue of its 'location' setting.

    In addition, when a RoomPart has an explicit 'location' setting, it will now automatically add itself to that location's 'roomParts' list. This means that you don't have to manually set both properties, which saves a little work and also makes your game easier to maintain, since you won't have to remember to make coordinated changes to both settings in your source code if you change the room part later.

    RoomPart and TravelConnectorLink now have a default sightSize of 'large'. This means that it's possible to examine these objects at a distance. Room parts are things like walls and ceilings that tend to be large and to contain large-scale details that would be realistically discernible at a distance - the details are typically things like doors and windows. Similarly, TravelConnectorLink (which you'd mainly use via its subclasses Enterable and Exitable) is for things like building exteriors, which likewise tend to have large-scale details.

    In cases where you create a RoomPart or an Enterable or Exitable that has fine-grained details that would be too small to see at a distance, and the object is visible from a separate top-level location linked by distance, you might want to override this to set the sightSize back to medium. When there's no distance-linked top-level location, this shouldn't be an issue, since there'd be no way to examine the object from a distance to begin with.

    RoomPartItem now overrides useSpecialDescInRoom() and useSpecialDescInContents() to return nil, and no longer overrides useSpecialDesc and useInitSpecialDesc.

    In the past, RoomPartItem overrode useSpecialDesc and useInitSpecialDesc (setting them to nil) in order to prevent room part items from being included in LOOK descriptions, but this had the bad side effect of preventing showSpecialDesc() from showing the initSpecialDesc. This change uses the more precise methods to select exactly where the special desc should be shown, without affecting the selection of which special desc to show.

    Thing.isListedInContents now calls useSpecialDescInContents(location) to make its determination. In the past, it called useSpecialDesc directly; this was incorrect because it didn't properly take into account the differentiation among contexts that the various useSpecialDescInXxx methods provide.
    The exit-list generator (exitLister.showExitsWithLister) now considers two destination locations to be equivalent based on the destination room object rather than the destination room name. This means that two distinct exit locations will now be listed separately even if they have the same name. For example, in the past, if the east and west exits led to separate rooms that both happened to be named "the hallway," the listing formerly read "east (or west), to the hallway", but will now read "east, to the hallway; west, to the hallway". The old approach of merging list entries based on name alone produced odd results in some cases, and didn't have any obvious benefits; the new approach should produce more predictable results.
    TIAction has a new method, retryWithAmbiguousIobj(), that lets an action handler specifically ask for disambiguation from a list of possibilities. This is the TIAction equivalent of the TAction method retryWithAmbiguousDobj(), and works the same way; the only difference is that the disambiguation list applies to the indirect object rather than to the direct object.
    The TAction and TIAction methods retryWithAmbiguousDobj() and retryWithAmbiguousIobj() can now be called before the "iteration" phase of the command execution, specifically during Action.beforeActionMain(), and they'll work properly: they'll ask their disambiguation question, then apply it to the entire iteration for the other object list. For example, if you call retryWithAmbiguousIobj() during beforeActionMain() to prompt for a new indirect object for a PUT IN command, the player's response will automatically be applied to the whole direct object list if the player specified multiple direct objects. In the past, it wasn't possible to call these "retry" methods during beforeActionMain(), so if they were used during commands with multiple objects in the other object slot, the result was that the "retry" - and its question to the user - was repeated for each object in the list.

    (This new flexibility involves two supporting changes. First, the various initForMissingXxx() methods in various Action subclasses now detect that the iteration over the object list hasn't begun yet, and they simply retain the entire object list (rather than the current iteration item, as they did in the past) for the retry. Second, to support the first change, the class PreResolvedAmbigProd now accepts an entire object list instead of just a single iteration element. These are internal methods that game code is unlikely to call directly, so the only visible effect of these changes should be the new flexibility in how the "retry" methods can be used.)

    In the past, initializeThing() was sometimes called multiple times in the course of dynamically creating an object (with 'new'). This happened when creating objects based on classes that inherited from Thing more than once (classes like this include Flashlight, Passage, Room, Chair, Bed, Platform, NominalPlatform, Booth, and UntakeableActor). This happened because the default constructor for any object based on multiple base classes simply inherits each of the base class constructors, one after the other; when more than one base class itself inherits from Thing, this results in multiple inherited invocations of the Thing constructor, which in the past resulted in multiple invocations of initializeThing() for the new object.

    Now, initializeThing() is only called once. The Thing constructor now tests a flag before calling initializeThing; it only calls the method if the flag isn't set, and it sets the flag after calling the method. This ensures that subsequent inherited constructor calls simply skip the call to initializeThing(). The constructor also skips the call to its own inherited base class constructor when the flag is set; this ensures that the vocabulary initializations in VocabObject are only invoked once per object.

    Room has a few command-handling enhancements, for rooms that have associated vocabulary. LOOK IN room is now treated the same as EXAMINE room; LOOK UNDER and LOOK BEHIND are refused ("You can't look under that"); SMELL and LISTEN TO room are now equivalent to simply SMELL and LISTEN; BOARD and ENTER are treated as redundant ("You're already in that"); and GET OUT OF is a little smarter about remapping, so that it only remaps to GO OUT if there actually is an OUT direction defined for the room, and fails with an error if not ("You'll have to say which way to go").
    NestedRoom.makeStandingUp() now leaves the actor's posture unchanged if the travel to the new location fails. It does this by noting the original posture before the travel attempt, and checking after the travel attempt to see if the actor is still in the starting nested room; if so, the routine restores the saved posture. This ensures that a failed travel attempt won't cause a posture change in the original nested location.
    NominalPlatform now overrides hideFromDefault() to always return true. Nominal platforms are meant to be used as internal objects and not to appear as simulation objects, so it's generally not desirable for them to be used as defaults.
    AccompanyingState had a problem that caused infinite recursion (leading to a stack overflow) when an actor in an AccompanyingState was explicitly moved via scriptedTravelTo(). The problem was simply that the actor attempted to accompany itself on its own travel, and that accompanying travel triggered a further accompanying travel, and so on. This has been corrected: actors in accompanying travel states now explicitly ignore their own travel for the purposes of accompanying travel.
    The library uses a separate default message in cases where an Actor is holding an object used in ENTER, BOARD, SIT ON, etc. In the past, the message was the rather awkward "You can't do that while the chair in in Bob." The new message is phrased "...while Bob is holding the chair" instead. The default message for non-Actors is unchanged.
    FOLLOW caused a run-time error if the follower was holding the NPC to be followed. This was because effectiveFollowLocation wasn't defined for ordinary objects. This has been corrected by defining Thing.effectiveFollowLocation to return the location's effectiveFollowLocation, or just 'self' if the object has no location.
    There are two new subclasses of ImpByeTopic, to allow you to differentiate between the two implicit ways of ending a conversation. BoredByeTopic handles cases where the NPC ends the conversation because of "boredom" (that is, inactivity in the conversation that exceeds the NPC's attentionSpan setting), while LeaveByeTopic handles cases where the PC simply walks away from the NPC in mid-conversation.

    BoredByeTopic and LeaveByeTopic extend the hierarchy that already existed with ByeTopic and ImpByeTopic. If there's an active ImpByeTopic and no active BoredByeTopic or LeaveByeTopic objects at the time of an implied "goodbye", the ImpByeTopic will be used for both cases (this also happens to be exactly the way the library worked in the past, before the two new subclasses were added, so this change won't disturb existing code). If there's an active BoredByeTopic as well as an active ImpByeTopic, the BoredByeTopic will be selected over the ImpByeTopic to handle "boredom" goodbyes; likewise, if there's an active LeaveByeTopic, it will be selected over an active ImpByeTopic to handle goodbyes triggered by the PC's departure.

    The "boredom" mechanism in InConversationState has been changed slightly. In the past, it was implemented directly in the takeTurn() method of InConversationState. Now, it's handled with an AgendaItem instead - specifically, a new subclass called BoredomAgendaItem, which the Actor and InConversationState classes manage automatically. This change should have no effect on existing code, since the new code implements the same behavior as the old version. The benefit of this change is that it makes it easier for a game to customize the boredom behavior, since it's now part of the more general "agenda" mechanism instead of being buried in ad hoc code inside the conversation state class.
    The suggested topic lister had a problem that showed up when several topic suggestions were present with the same listing name, and some of the topics were inactive while others were active. The lister automatically removes redundant entries from suggestion lists by removing multiple items with the same name, but in the past, the lister sometimes incorrectly removed the active elements instead of the inactive ones, effectively eliminating the suggestion entirely from the list. This has been corrected: the lister now only removes a redundant suggestion if there's another active suggestion with the same listing name.
    InitiateTopic no longer sets any pronoun antecedents when triggered. (It did set its match object as a pronoun antecedent in the past. This was undesirable because an InitiateTopic is triggered by internal calculations in the game, not by anything the player has done.)
    ConvAgendaItem now checks to make sure the player character is present before declaring itself ready. This ensures that an NPC won't attempt a conversational agenda item unless the PC is actually present to hear what the NPC has to say. (This change is in ConvAgendaItem.isReady.)
    Actor.executeAgenda() formerly marked an agenda item as done if invoking the item threw any sort of exception. Now, this is only done on a run-time error (a RuntimeError exception); in cases of other exceptions, the item's doneness isn't changed. The original purpose of the doneness change was to reduce debugging hassles in cases where an agenda item encountered an error; in such cases, if the item wasn't marked as done, the scheduler would end up invoking the item in an infinite loop, because it would look perpetually ready to run. However, doing this on all exceptions interfered with certain non-error cases where exceptions were used to jump out of the action handling. Limiting the caught exceptions to runtime errors should retain most of the intended benefits while avoiding the problems with a more general error catcher here.
    Actor.initiateConversation() now handles a nil 'state' argument slightly differently. In the past, if 'state' was nil, the actor's state was simply left unchanged. Now, the method will switch the actor to the state returned from the current state's getImpliedConvState() method. In most cases, the net effect is exactly as before - i.e., the actor's state is left unchanged - because the default getImpliedConvState() simply returns 'self'. However, ConversationReadyState overrides getImpliedConvState() to return the associated InConversationState. This change makes it easier to initiate a conversation when using an actor with conversational states, since it will generally pick the correct conversational state automatically.

    Note that the ActorState method getImpliedConvState() is new with this change.

    Due to a bug, <.convnode> tags displayed inside npcGreetingMsg methods weren't handled properly if the greeting was displayed due to NPC initiation of the conversation (via npcInitiateConversation()). This has been corrected.
    ConvNode.canEndConversation() can now return a special value, blockEndConv, that indicates that the actor said something to force the conversation to keep going. You should always use this if you display a message in the routine and you want to prevent the conversation from ending. Returning blockEndConv is almost the same as returning nil from the method, but has the additional side effect that the caller will call noteConvAction() on the other actor, to prevent this actor from generating any further scripted remarks on the same turn.
    When the library displays a parser error message for a command directed to an NPC, it now shows the error message in a neutral sense context. This ensures that the message is displayed even if the NPC is in a remote location (this could be the case if we're talking over the phone, for example). In the past, the parser message was generated in the NPC's sense context, so the NPC was in a remote location that wasn't in scope to the PC, the parser message was suppressed, resulting in a "Nothing happens" message or the like.
    When a command of the form "actor, xxx" is entered, and the portion after the "actor" phrase is unparseable, the parser now attempts to parse at least the "actor" part to determine if the command is being directed to an NPC. In the past, the parser didn't do this; the actor phrase is part of the basic sentence grammar, so if the parser failed to match the rest of the basic sentence grammar, it didn't bother trying to figure out if a target actor was included. To find the target actor, the parser now makes a second attempt at parsing a command to see if it matches a very basic sentence syntax that only includes the target actor specification; if it can match the syntax, and resolve the noun phrase to an in-scope actor, the parser takes the result to be the target actor.

    The main result of this change is that a few of the low-level parser message methods - askUnknownWord, specialTopicInactive, commandNotUnderstood - are now invoked on the target actor when one is present in the command. In the past, because the parser didn't even figure out that a target actor was present in these cases, these methods were always invoked on the player character actor. With this change, it's now possible to customize a NPC's responses for unknown words and command phrasings individually by NPC.

    The library now gives a replacement action zero time if it's replacing an action that itself has zero time. This corrects some timing anomalies (particularly with respect to NPCs) that showed up in certain unusual cases where replaceAction() was used in the course of an action that itself was being run as a nested action.
    A new parser option lets you cancel remaining commands on the command line when an action fails.

    To implement this, Action.afterActionMain() checks to see if the action failed, as indicated by a 'reportFailure()' message in the course of the action handling. If the action failed, and gameMain.cancelCmdLineOnFailure is set to true, afterActionMain() throws a CancelCommandLineException. This exception is in turn caught in executeCommand(), which cancels any remaining commands on the command line and simply proceeds to the next turn.

    In addition, the new playerMessages method explainCancelCommandLine() lets you display an explanation when a command line is canceled due to the failure of a command. executeCommand() invokes this new method when it handles a CancelCommandLineException if there are in fact any remaining tokens on the command line. (This check for remaining tokens skips the explanation when there's nothing left on the command line to cancel, as the cancellation obviously has no effect in such cases.) This new message method doesn't display anything by default; it's just a hook that you can use if you want to provide an explanation. Note that you'll probably want to show this explanation only once per game, rather than every time a command is canceled (you can use a flag property to do this - if the flag is nil, show the message and set the flag to true; otherwise skip the message).

    The default setting for gameMain.cancelCmdLineOnFailure is nil. This provides the traditional handling, which simply continues executing any remaining commands on the command line even when an action fails.

    The reason this new feature is an option rather than simply being the default new policy is that neither possibility is ideal in every case. On the one hand, continuing to execute commands after an action has failed can lead to confusing results: the failure of the earlier command can leave things in a state other than what the player was anticipating, which could change the behavior of subsequent commands. So the argument in favor of canceling remaining commands is that it avoids this possible source of player confusion by halting processing once it appears that the player's thinking is out of sync with the game's internal state. On the other hand, exactly what constitutes failure might not always be apparent to the player, so halting halfway through a command line might sometimes appear arbitrary to the player, or even buggy. The argument in favor of continuing to plow through the rest of the command line like nothing happened, then, is that it's simple and consistent. A prime virtue of any UI is predictability, so that the user has an easier time forming a working mental model of the software, and the most predictable behavior is to unconditionally execute everything on the command line. Further, given that multi-level UNDO is available by default in tads3 games, any unintended effects from extra commands can always be manually undone as soon as the player realizes what happened. The arguments on both sides are valid, so the library leaves it up to the author to set the game's policy. (It's even been suggested that this ought to be left up to the player, via a command that selects the mode, but I think this would be overkill, so for now this is just an author-controlled option. Games are free to add their own command to let the player control it, of course; it's just a matter of flipping the cancelCmdLineOnFailure setting at run-time.)

    The Action class has a new method, checkAction(), that is called just before the existing execAction() method. TAction and TIAction define the new method, and perform the calls to the direct and indirect object 'check' methods in this new method rather than in execAction(), as was done in the past.

    This change should have no effect on existing code, since it's a simple internal rearrangement. The benefit is that it's now possible for game code to call the 'check' phase of a verb explicitly, without also invoking the 'execute' phase.

    OopsIAction is now defined as an IAction. (In the past, it was based directly on Action, which was problematic if an OopsIAction grammar match was used in certain contexts, such as CommandRanking.sortByRanking.)
    ObjectPreCondition now uses the same ordering as the underlying condition.
    During calls to the "verify" methods for remapped actions (i.e., actions mapped to different actions using remapTo), the library now sets gAction to the remapped action. In the past, during the initial remap testing stage, the library left gAction with the original action that triggered the remapping. This gives game code more consistent information during the verify phase.
    VocabObject now defines the methods isOwnedBy(obj) and getNominalOwner(). This allows player input to refer to non-physical objects, such as topics, using possessive syntax.
    The INSTRUCTIONS command is now ignored for the purposes of UNDO and AGAIN.
    The parser no longer chooses Unthings as default objects in cases where noun phrases are missing in player input. (This is handled via Unthing.hideFromDefault().)
    The new Thing method setGlobalParamName() lets you add a global message parameter name dynamically. In the past, there was no straightforward way to do this; you had to manually update the message builder's internal table of names in order to add a new name. This method takes care of all of the necessary internal table updates.

    You only need to use this method if you want to add or change a parameter name dynamically at run-time. The library still automatically initializes the message builder's tables for globalParamName settings defined at compile-time.

    The parser now marks a plural-phrase match for a direct or indirect object as "unclearly disambiguated" if the phrase also matches a singular noun for an object that's also in scope. This flag makes the parser generate an extra message to notify the player that the parser chose an object from a potentially ambiguous set:

      >show bob the rags
      (the piece of cloth)
      Bob does not appear interested.
    

    If the parser's decision was wrong, the message will alert the player, so that the player will know to try rephrasing the command rather than being left with the impression that the intended command didn't work.

    The event manager (the part of the library that manages fuses and daemons) now automatically disables a daemon or fuse that throws an exception out of its main execution method. The reason this is important is that an infinite loop would otherwise occur in many cases: if the object remained active, the event manager would re-invoke it after the exception, and in most cases the re-invocation would simply encounter the same exception, at which point the event manager would invoke it once again, and so on ad infinitum.

    Note that this change only comes into play when a fuse/daemon throws an exception out of its main method. It won't affect a daemon/fuse that merely encounters an exception in the course of its processing, as long as the fuse/daemon catches and handles the exception.

    A new class, OneTimePromptDaemon, makes it easy to set up a prompt daemon that only fires once. It works like an ordinary prompt daemon, but removes itself from the active event list as soon as it fires. This can be handy for cases where you want to defer some non-recurring processing until just before the next command prompt.
    Thanks to Greg Boettcher, the English library's default messages now use typographical ("curly") quote marks and apostrophes. In cases of open/close quote marks, these are generated with <Q>...</Q> markups; in cases of apostrophes, &rsquo; entities are used.

    This change is purely cosmetic, so it should require no changes to existing game code. It shouldn't even affect test scripts (where you run an input script and "diff" the output against a reference log) - by default, the log file mechanism by default generates transcript output in plain ASCII, and the curly quotes are mapped to ordinary straight quotes when rendered in plain ASCII.

    The library now attempts to be as precise as possible when announcing objects chosen in vague disambiguations, as defaults for missing noun phrases, and for multiple objects to a verb. In the past, the library simply used the base name of the object being announced, but this didn't take into account the various things that the parser can use to distinguish objects on input, such as lit/unlit state, location, and owner. The library now generates these object announcements using the same Distinguisher mechanism it uses to resolve ambiguous noun phrases in input, ensuring that the generated names are as precise as possible in distinguishing announced objects from others in scope.

    To accomplish this, the announcements call upon a new Thing method, getInScopeDistinguisher(). This method looks at the Distinguisher objects associated with the object to be announced, and tries to find one that can distinguish the object to be announced from every other object in scope. If it finds one, it returns it. If it fails to find one, it returns the distinguisher that does the best partial job - that is, the one that distinguishes the object from the largest number of other in-scope objects. (The method never returns nil; in the worst case, it simply returns basicDistinguisher, which distinguishes objects based on their disambigName properties.) The announcements then use the returned Distinguisher to generate the announced name.

    Note that in the simplest case, this change results in the disambigName being used in object announcements; in the past, the base name was used. In most cases, this won't cause any change in game behavior, since the disambigName for most objects is just the base name anyway.

    The library now suppresses vague disambiguation announcements for objects specified with indefinite articles when the announcement would be redundant. That is, if there's no Distinguisher that can tell apart any of the possible matches for an object specified with an indefinite article, the library doesn't bother making the announcement, because it would add no useful information for the player. For example, in the past, you might have seen an exchange like this:

       >take a silver coin
       (the silver coin)
    

    The parser was making the announcement because it had chosen arbitrarily from one of several silver coins. But since all of the possible matches were indistinguishable anyway, the announcement doesn't help the player see which one in particular was chosen. The library now suppresses the message in this case. Note, however, that if it's possible to distinguish any of the possible matches from one another, you'll still see a message. For example, if there's a silver coin and a gold coin present, and the player types TAKE A COIN, the library will announce which one (gold or silver). Similarly, if there's a silver coin on the table and another on the floor, and the player types TAKE A SILVER COIN, the library will mention the location of the one chosen: "(the silver coin on the floor)", for example.

    The English library can now correctly generate a plural name for an object whose name includes an "of" phrase; for example, the library now correctly generates the plural for "piece of paper" as "pieces of paper". In the past, the library pluralized the last word of the entire phrase, so the result in this example would have been "piece of papers". The new algorithm when there's an "of" is simply to pluralize the last word before the first "of", using the same rules as for a phrase without "of".
    In the past, the English parser treated a command of the form GIVE adjective noun as though it meant GIVE noun TO adjective. This was almost never what the player actually meant; the player almost always really meant GIVE object TO the current interlocutor, where object is named adjective noun. The parser now applies that more likely interpretation when confronted with this syntax. The same applies to SHOW.

    (As part of this change, a new parser class was added: ImpliedActorNounPhraseProd. This is a subclass of EmptyNounPhraseProd that works almost the same way, but doesn't apply a grammar ranking penalty for the missing noun phrase if a default interlocutor can be identified. The single-noun-phrase grammars for GIVE and SHOW use this new class, ensuring that they're chosen over the non-prepositional two-noun-phrase phrasings whenever a default interlocutor is present.)

    In the past, the English parser treated the word "of" as a non-weak token when matching noun phrases of the form "x of y". This meant that if a noun phrase consisted only of weak tokens and "of", the parser matched it. This has changed; "of" is now ignored for the purposes of the weak-token test, so a phrase consisting only of weak tokens and "of" is now considered weak overall.
    A bug in the English library caused run-time errors in some cases involving TopicAction verbs. The problem was that some of the TopicAction message generators assumed that they were the current active verb, which isn't always the case. This has been fixed.
    The English library defines the new verb phrasings ASK actor topic and TELL actor topic; these are phrasings for the new actions AskVague and TellVague, respectively. These are defined entirely to provide more helpful error messages in cases where the player enters an ASK or TELL verb without an ABOUT phrase. The default handling for the new actions simply displays an explanation of the proper ASK ABOUT or TELL ABOUT phrasing. Some players have been seen to misinterpret custom phrasings that show up in SpecialTopic prompts, such as "ask bob why", as general-purpose command phrasing, and then attempt to use similar phrasing elsewhere in the game. This results in "invalid command" errors from the parser, of course, but the standard error messages were often unhelpful with this particular kind of phrasing error. The new handlers are meant to improve on the standard error messages for this common case.
    The English library now treats the command phrasing CLIMB IN/INTO/IN TO as a Board action, and CLIMB ON/ONTO/ON TO as a StandOn action. In the past, both forms of CLIMB were defined for both of these actions, which made the choice of which action matched the syntax arbitrary and unpredictable. This change ensures that each of these phrasings is unambiguously assigned to only one action.
    The English library now tries a little harder to figure out whether a command of the form ENTER text means ENTER text ON some implied keypad-type object, or GO IN object. In the past, the library always used the GO IN interpretation. Now, the library assumes the ENTER ON keypad interpretation if there's a suitable default object present; if there isn't, the library reverts to the GO IN interpretation.
    The English library formerly allowed any object to match the pronoun "them"; it now limits "them" matches to (a) objects used in lists in player input, as in TAKE IRON KEY AND BRASS KEY, and (b) objects marked as having plural usage via isPlural.
    The parser now lowers the grammar match ranking for any phrasing involving a plural in a noun slot requiring a single object. This makes the parser a bit smarter about picking the right vocabulary interpretation in cases of nouns that have both singular and plural usage, such as FISH and SHEEP.
    In the English library, the isHim and isHer properties are now taken into account for arbitrary Thing objects in parameter substitutions in messages. In the past, gender was only taken into account for Actor objects; non-Actor objects were always "it" or "them" in messages.
    In the English library, the Actor methods theName, theNameObj, theNamePossAdj, theNamePossNoun, aName, and aNameObj now properly handle the first-person plural case ("we", "us", "our", "ours", etc). In the past, a plural Actor with first-person referralPerson incorrectly used the singular first-person pronouns.
    The PourOnto verb in the English library now correctly uses onSingleNoun as its indirect object response phrase, matching the regular grammar for the verb. (In the past, it incorrectly used withSingleNoun as the response phrase.)
    The English library now tries harder to avoid duplicating vocabulary in the lists it constructs from vocabWords properties during initialization. First, VocabObject.inheritVocab() now avoids redundantly scanning vocabulary inherited from superclasses. Since this routine's whole point is to explicitly scan the class hierarchy for each object and add the vocabulary inherited from each superclass, it needs to bypass the normal automatic inheritance mechanism, which it now does. Second, initializeVocabWith() now explicitly removes duplicates from each word list it updates, to avoid duplication in cases where an object explicitly defines the same vocabulary word at multiple inheritance levels.
    The new function isListSubset() determines if one list is a subset of another list: that is, if every element of the first list also appears somewhere in the second list. (This function was formerly defined as a method in the Lister class, but it's generically useful and wasn't tied in any way to Lister, so it's been broken out as a function to make it more readily accessible to other code.)
    The library functions main() and mainRestore() have been enhanced to catch QuittingException signals. These functions, of course, are the main entrypoints for the entire program - the former is for normal start-up, and the latter is for the special kind of start-up that restores a saved state directly from the operating system shell, such as when the player launches the game by double-clicking a saved-state file on the Windows desktop. Catching QuittingException in these functions allows game code to throw QuittingException just about anywhere, without worrying about the context. In the past, this exception was only usable after the main command loop started; in particular, it wasn't usable from startup or initialization code (such as the code that displays the game's introductory messages), because there was no handler to catch the exception in effect while that code was running.
    3.0.9

    Released 8/29/2005

    Minor compatibility-breaking change: In the English library, the ThingState template now uses "+" rather than "@" to introduce the listingOrder property value. The choice of punctuation here is pretty arbitrary; the reason for the change is that a consensus emerged among users that "+" would be easier to remember in this template because the other library templates generally use "+" to mark integer slots.

    If you have existing game code that defines any ThingState objects using the old template, you'll need to change the definitions to use "+" instead of "@". Alternatively, you could put your own definition of the old ThingState template into a common header file that your game use, in which case you wouldn't have to change any of your existing object definitions; but you're probably better off just changing your code to use the new convention, since defining your own template could cause some slight confusion for other users if you should share your code in the future.

    Slight risk of incompatibility: Thing.getDropDestination() must now accept a nil value for the 'obj' argument. A nil value of 'obj' asks for the drop destination for a "typical" object, rather than for any specific object. In the past, it wasn't specified that 'obj' could be nil, and the library never directly called the method with a nil 'obj' value, so it's possible that some existing game code might assume that 'obj' is non-nil. You should do a quick scan of your game code for getDropDestination() definitions to ensure that they'll work properly when 'obj' is nil. In particular, make sure that you don't evaluate any properties of 'obj' without first checking that 'obj' is non-nil:

       getDropDestination(obj, path)
       {
          if (obj.isReallySmall) ...;   // Don't do this!!!
          if (obj != nil && obj.isReallySmall) ...; // Do this instead
       }
    
    Slight risk of incompatibility: The default value of Event.eventOrder is now 100 (it was formerly 50). This default is essentially arbitrary (since it's only important for relative ordering), but a value of 100 is more consistent with other similar ordering defaults elsewhere in the library.

    In addition, the eventOrder of the standard sense-change daemon is now 500, to ensure that the sense-change daemon runs after most other events. The library automatically creates the sense-change daemon at game start-up; this is the daemon that displays messages about new, ongoing, or ending sounds and odors. In the past, this daemon had default priority, so its order relative to other fuses and daemons was arbitrary. If another fuse or daemon ran after the sense-change daemon, and made some change that affected the sensory environment, a message about the change wasn't displayed until the following turn, since the chance had already passed to generate the message on the turn during which the change actually occurred. This change ensures that the sense-change daemon runs after most other events, so any changes that occur in daemons and fuses will be reflected in the sense-change update for the same turn.

    If you've explicitly set eventOrder for any fuses or daemons in your game, you might need to adjust the values you set. In particular, if you set an eventOrder value between 50 and 100 for an event object to ensure that the event is processed after events that inherit the default eventOrder, you'll need to raise that value above 100. You should also consider if it's desirable for those events to run after the sense-change daemon, as they would have in the past (since the sense-change daemon used to have default priority), and if so, raise their eventOrder values to above 500. Finally, if you have an existing eventOrder that's over 500, you might want to consider whether you still want the event to run after the sense-change daemon; if not, you should lower the value to under 500.

    Possibly incompatible change: The parameter lists of some of the internal library routines that handle point-of-view have changed. These aren't likely to affect any existing game code, since these are mostly used only inside the library itself, but you should check your code for any mentions of these names to be sure. You'll need to adjust any code in your game that either calls or overrides any of these methods or functions:
    Very slight risk of incompatibility: In the following methods, the parameter now gives the point-of-view actor instead of the point-of-view object. The POV actor and object are usually the same anyway, so the effects on existing code should be minimal. If you actually need the POV object, you can get it with the getPOV() function.
    The new function getPOVActor() returns the "point-of-view actor." This is the actor who's doing the looking that is, the actor who's performing the action (LOOK AROUND, EXAMINE, SMELL, etc) that's generating the current description. This is usually, but not always, the same as gActor, and it usually, but not always, returns the same object as getPOV().

    The point-of-view actor can differ from gActor in cases where game code explicitly generates a room description from the point of view of one actor in the course of processing a command given to a different actor. In these cases, gActor will be the actor who's performing the command, and getPOVActor() will return the actor who's generating the description.

    The point-of-view actor can differ from the point-of-view object (the object returned by the getPOV() function) when the actor is observing the location being described through an intermediary object. For example, if we're generating a description of a remote room because an actor is examining a closed-circuit TV monitor showing the remote location, the point-of-view actor will be the actor who's looking at the monitor, while the point-of-view object will be the camera in the remote location. Note that the POV is the camera, not the monitor: the POV is intended to be the object that's physically absorbing the light rays, or other sensory equivalents, from the surroundings being described.

    The library now selects remote descriptions for rooms and other objects a little differently than before. In the past, the remoteness of the point-of-view object was the deciding factor - that is, remote descriptions were used when the POV wasn't in the same room as the object being described. Now, the library bases the decision on the point-of-view actor, not the POV object: if the actor who's doing the looking isn't in the same room, the remote description is used. This means, for example, that the remote description will be generated when observing a room through a closed-circuit TV system: even though the POV (the camera) is in inside the room being described, the actor who's observing the TV monitor is in a different room, so the remote description is used.

    This change is implemented in Thing.showSpecialDescWithInfo() and Thing.showSpecialDescInContentsWithInfo().

    The method Thing.lookAroundWithinContents(), which is the part of the room describer that lists the room's contents, had a bug in past versions that caused a single object to be listed twice in some unusual cases. In particular, if the room itself had brightness 1, and it contained an object that also had brightness 1, the object was listed twice: once in a list starting "In the dark you see," then again in a list starting "The room contains." The same problem occurred in a dark room if an object with brightness 1 was inside a container with brightness 1. This has been fixed.
    A new mechanism in Actor allows objects to be explicitly excluded from the "look around" description of a room. To exclude an object, call the method excludeFromLookAround(obj) on the actor, passing in the object 'obj' to be excluded. The object will be suppressed from room descriptions until further notice - it won't show up in any of the visible-object lists, including special descriptions, room contents lists, or nested contents lists. To remove an object from the exclusion list, call unexcludeFromLookAround(obj).

    The exclusion that this method applies is over and above any other listing-selection mechanisms, such as obj.isListed. It's intended mostly for ephemeral exclusions that are applied other than by the object itself, since the object itself can usually more easily control its own listability via isListed and the like.

    In Thing.adjustLookAroundTable(), the point-of-view actor is no longer removed from the table; instead, just the point-of-view object is removed. In cases where the POV and actor differ and the actor is in sight of the POV, we usually want to include the actor in the generated description.

    In addition, the method now calls adjustLookAroundTable() on the actor and POV objects, giving them a chance to make further adjustments to the table.

    Suppose we have an NPC who's in scope but out of hearing range, such as when the NPC is on the other side of a transparent, soundproof window from the player character. If the player addresses a command to the NPC, the library now uses the same error message that it uses for ASK ABOUT and other conversation commands in the same situation.

    (This corrects a bug that used to occur. In the past, the library attempted to let the NPC respond, rather than generating a parser error. The problem with this approach was that the soundproof barrier stopped the PC from hearing the NPC's response, thus the library could only report that "Nothing obvious happens." Now that this is handled as a parsing error instead of a conversational response, the more helpful "Bob does not appear to hear you" is displayed.)

    Thing.isListedInRoomPart has changed slightly. In the past, an object was listed (i.e., shown in a miscellaneous list of portable contents) as part of an EXAMINE FLOOR (or EAST WALL or the like) if the object was nominally part of the room part (that is, nominally on the floor, or on the given wall, etc) and the object was normally listed the same way in a LOOK AROUND command. Now, the object is listed if it's nominally part of the room part and the new RoomPart method isObjListedInRoomPart() returns true.

    The new method RoomPart.isObjListedInRoomPart returns true by default if the object does not have a special description for the purposes of examining the room part - that is, if the object's useSpecialDescInRoomPart returns nil. We don't want to include the object in the miscellaneous list if it has a special description because the special description will be automatically shown - it would thus be mentioned twice if we included it in the miscellaneous list as well.

    Floor overrides isObjListedInRoomPart to list an object only if it's listed normally - that is, the object's isListed returns true. This is the old behavior that applied to all room parts, but now it only applies to Floor objects. The reason we treat Floor differently is that everything that's directly in a room is by default nominally on the floor in the room, so if we didn't limit the listings like this in the case of EXAMINE FLOOR, we'd include all of the internal fixtures and components of the room, which is generally undesirable.

    The main purpose of this change is that it makes it a little easier to set up Fixture-type objects that are described as being situated on walls, ceilings, and the like. Now, all that's necessary is to give the object a specialNominalRoomPartLocation - even if the object isn't normally listed in a room description, as is the case for a Fixture, we'll now list the object when we examine its room part as long as it doesn't have an explicit special description for the room part.

    The new mix-in class RoomPartItem can be used to create an object that shows its special description when an associated room part is described. This is useful for things like doors, windows, ceiling fans, and other objects attached to the room. The main thing this class does is to make the object show its special description when its associated room part is examined (as in LOOK AT EAST WALL), but not in the main room description (as in LOOK AROUND). This is desirable because this kind of object is usually not remarkable enough to call out with a special description in the overall room, but does warrant a mention when its associated room part is explicitly examined.
    SensoryEmanation has a new property, isEmanating, that lets you turn the object on and off. By default, is Emanating is true; if you set this to nil, it shuts off the emanation. This makes it easier to make sounds and odors start and stop, which is often useful when the emanations are associated with dynamic objects such as actors.
    Settable has a new method, canonicalizeSetting(), that converts a proposed new setting to a "canonical" format. Settable now uses this to process the player's input before passing the value to isValidSetting() or makeSetting(), ensuring that these routines don't need to perform any further format conversion of their own.

    This method makes it much easier to handle superficial formatting variations in the player's input by centralizing the conversions to this single routine. In the past, both isValidSetting() and makeSetting() had to handle formatting variations since they received the raw player input.

    Settable's default implementation of canonicalizeSetting() doesn't do anything - it just returns the original player input unchanged. NumberedDial overrides the method to convert spelled-out numbers to numeral format and to strip off any leading zeroes from an entered numeral format; LabeledDial overrides it to convert the input to the exact case of the matching entry in the list of valid labels.

    The new library routines parseInt() and parseIntTokens() parse a string or token list (respectively) that contains a spelled-out or digit-string numeric value. These routines parse the input and return the corresponding integer value, or nil if the input can't be parsed as a number. For example, parseInt('one thousand and twenty-four') returns 1024, parseInt('72') returns 72, and parseInt('dusty tome') returns nil.
    The English library now incorporates Michel Nizette's Past Tense extension. This enables the author to select the tense - present or past - used in library messages, and even allows you to change the tense dynamically during the game.

    The 'master switch" for the active tense is gameMain.usePastTense. Set this to true to activate past tense in library messages. The default is nil, which selects present tense.

    A number of new message parameters and verbEndingXxx properties help in writing messages that can change tense. The standard library messages defined in en_us/msg_neu.t use these new features to adapt automatically to the current tense.

    See the comments in en_us/en_us.t for full details.

    The library has a new framework for "global defaults," which lets the user save certain preference settings as default values for future sessions. These saved defaults are global: they apply to any new game subsequently loaded, provided that the new game supports the new global defaults system. Games compiled with library versions prior to 3.0.9 didn't support this new system, but games compiled with 3.0.9 or later will support it by default, as long as an author doesn't explicitly disable it for some reason. The settings are saved to a file whose name and location are determined by the interpreter (and which might vary by system, to conform to local conventions).

    Two new commands let the user control the global defaults: SAVE DEFAULTS saves the current settings to the configuration file, and RESTORE DEFAULTS explicitly loads the file and applies its settings. These commands both list the full set of saved settings, so that the user can easily tell exactly which settings are affected.

    By default, the library uses the new global settings framework for the VERBOSE, FOOTNOTES, NOTIFY, and EXITS settings.

    The new mechanism is implemented mostly in the new file settings.t. The main API is exposed through the new settingsManager object, and the command-line user interface is implemented in the new settingsUI object. The new SettingsItem class is used to encapsulate individual configuration variables. If you want to create your own extensions to the set of global default variables, simply create a SettingsItem object for each new variable you want to add. The settings framework will automatically find your new SettingsItem objects, and save and restore them as needed.

    The new macro gSetSeen(obj) marks an object as having been seen by the player character.
    The new macro gTopicText returns a string containing the literal text of the current topic phrase, when the current action contains a topic phrase. The text returned is as the user typed it, but converted to lower case.
    The LiteralAction and LiteralTAction class are now based on a new common mix-in base class, LiteralActionBase. This is parallel to the structure of TopicAction and TopicTAction, with their common mix-in base class TopicActionBase. This new arrangement shouldn't at all change the behavior of LiteralAction or LiteralTAction, and should be entirely transparent to existing code.

    The change has two main benefits. First, it eliminates some code duplication, by replacing a few bits of code that were effectively duplicated in LiteralAction and LiteralTAction with common versions that both classes now inherit from LiteralActionBase. Second, game code can now easily determine if an action involves any sort of literal phrase, by testing gAction.ofKind(LiteralActionBase).

    The new Thing method failCheck() makes it a little more convenient to write check() routines. check() routines frequently use this pattern:

       check()
       {
         if (condition)
         {
           reportFailure('You can\'t do that for some reason.');
           exit;
         }
       }
    

    The failCheck() routine encapsulates the display of the failure report and the termination of the action with 'exit'. Using failCheck(), you can rewrite the code above more concisely, like so:

       check()
       {
         if (condition)
           failCheck('You can\'t do that for some reason.');
       }
    
    Occluder.occludeObj() has a new default implementation. By default, the method now asks the object being tested for occlusion to make the decision, by calling the object's isOccludedBy() method.

    Thing provides a default implementation of the new isOccludedBy() method, which simply returns nil to indicate that the object is not occluded by the given Occluder.

    Existing code won't be affected by this change, since any existing Occluder code already overrides isOccludedBy() to make the per-object occlusion decision. The reason for this change is that it's sometimes easier to decide about occlusion in the individual objects rather than in the Occluder; the new default Occluder.occludeObj() makes it easer to write the code that way by providing a pre-defined framework that already works that way.

    In the past, if a Container was used as the indirect object of PUT IN or POUR INTO, the library applied an objOpen precondition to ensure the container was open. The library now applies the objOpen condition only if the actor performing the action isn't itself inside the container. The reason for the objOpen condition is that putting or pouring things into a container requires access to the container's interior, and a container's interior is only reachable from outside the container when the container is open. However, when an actor is inside a container, the actor can reach the inside of the container whether or not the container is open, so there's no need for the objOpen condition.
    Fixture, Component, and Immovable now define custom handlers for PUT BEHIND that are consistent with their handlers for PUT IN, PUT ON, and PUT UNDER.
    On an actor's attempt to take an item that was inside something the actor was already carrying, Actor.tryMakingRoomToHold() formerly double-counted the item's weight in enforcing the actor's carrying limit - it counted the item first for the weight of carrying the new item itself, then again for its contribution to the weight of its old container (because the old container was in the actor's inventory). The routine now correctly counts the weight only once. (The new algorithm uses the whatIfHeldBy() method, which actually tests the proposed new arrangement to calculate the new weight carried and thus yields reliable results no matter what configuration previously existed.)
    TAKE ALL now tries to take everything that's directly in the actor's current "drop destination", or that's directly in the actor's direct container, or that's in any intermediate container between the two. The former behavior was to take everything in the actor's direct location only. This change improves the behavior when the actor is in something like a chair or bed. In these sorts of nested rooms, items dropped usually land in the enclosing room; the new behavior ensures that a TAKE ALL command attempted while in such a location will include items that were just dropped while in the same location. With the former behavior in this situation, TAKE ALL missed the items in the drop location, which was likely to confuse players.
    NonPortable now defines a default weight and bulk of zero. Since these objects aren't meant to be carried or moved around by actors, their weight and bulk are essentially irrelevant; but the previous non-zero defaults occasionally had undesirable side effects in cases where a non-portable was a component of another object or among another object's contents.
    An attempt to give an object to an NPC when the NPC is already holding the object in question is now ruled out as illogical ("Bob already has that").
    The library now provides commands that let the player control the display of the exits listing in the status line. EXITS ON now turns on both the status line and room description exit listings, and EXITS OFF turns them both off. The new command EXITS STATUS (which can also be entered as EXITS STATUSLINE or EXITS STATUS LINE) turns on the status line exit listing only, and EXITS LOOK turns on the room description exit listing only.

    In addition, these new commands are now described in the standard instructions.

    The "probabilistic firing" feature of the RandomEventList class is now available in a separate mix-in class, RandomFiringScript. This new class can be mixed into the superclass list of any Script subclass to add the random firing feature.

    RandomEventList and ShuffledEventList are now based on the new RandomFiringScript. This doesn't change their behavior; existing code based on these classes should continue to work unchanged.

    The SyncEventList class now delegates calls to its scriptDone() method to its master list object. This ensures that any explicit calls to a SyncEventList's scriptDone() method will be handled via the master list, which is consistent with SyncEventList's handling for most of its other methods.
    The library now has a better response to the FOLLOW command if the target NPC departed the player character's present location while the PC was somewhere else. Suppose the PC sees Bob in the living room, then goes to the dining room; while the PC is in the dining room, Bob departs for a third room (due to some scripted activity). The player then returns to the living room. At this point, it's not possible to FOLLOW BOB, since the PC didn't see where Bob went. This much hasn't changed; what's changed is the library's default response if player types FOLLOW BOB in this situation. In the past, the response was "The last place him was the living room," which is true but not very helpful (and a little non sequiturish). Now, the response is "You're not sure where he went from here."
    NominalPlatform now returns its location's effectiveFollowLocation as its own effectiveFollowLocation. A nominal platform isn't meant to be part of the visible structure of the game world, so, for FOLLOW purposes, a character on a nominal platform is effectively in the enclosing room as far as the other characters in the room are concerned.
    A bug in the parser caused possessive pronouns to be treated differently in a TopicTAction when the direct object phrase was missing than when it was provided. For example, ASK BOB ABOUT HIS KEY was treated differently from the abbreviated equivalent A HIS KEY. This has been corrected.
    The parser now drops its preference for untruncated name matches when resolving an object in "global scope," and instead treats truncated and exact name matches on an equal footing in these cases. This change is analogous to the global scope changes for plurals and adjective-ending phrases introduced in 3.0.6q and 3.0.6p. See the adjective-ending notes from 3.0.6p for details on what global scope is and why its rules on matching are more lenient.

    The practical effect of this change is that topic words will now match properly even when there are longer and shorter versions of the same vocabulary defined for various objects. In the past, if one object was defined with a six-letter noun, and another object had a seven-letter noun with the same first six letters as the other object ("coffee" and "coffees", say), the parser filtered out the seven-letter match in favor of the six-letter match, even though both objects ought to be equally valid in global scope. With the change, both objects are now properly kept as possible resolutions when parsing a topic phrase.

    The parser now selects Collective objects instead of their corresponding individuals when matching "all" and "all except list" noun phrases.

    Whether it's better to select the Collective or the individuals probably varies according to context, since the "right" one is the one the player intends in a particular command, and that will likely vary from one command to the next (and from one player to the next). But the design of the parser requires that we choose one interpretation or the other across the board; context sensitivity isn't an option. Even if context sensitivity were an option, we might still not have enough information to correctly read the player's mind every time. This change - matching the Collective object instead of the individuals - seems more likely to yield intuitive results more of the time than the old behavior.

    The parser now applies the normal filterResolveList() filtering when resolving a target-actor phrase (such as the "bob" in "bob, go north"), the same as it does when resolving other kinds of noun phrases. In the past, the parser omitted this filtering step for target actors, which didn't give objects like Unthings and Collectives a chance to do their special filtering.
    When the player enters a command of the form TAKE ALL FROM iobj, the TakeFrom action now lets the indirect object determine what ALL means. It does this via a new method, getAllForTakeFrom(scopeList). Thing implements this method using almost the same behavior that TakeFrom used in the past: it returns a list of everything in scope that's directly inside 'self' and isn't a component of 'self'. The old behavior simply returned everything in scope that was directly inside the indirect object.

    ComplexContainer overrides getAllForTakeFrom() to return everything that's in scope and that's directly in (a) the sub-container or sub-surface, if the object has either, or (b) the sub-rear-surface or sub-underside, if the object doesn't have a sub-container or sub-surface; and as with Thing's version, this list is then limited to objects that aren't components of their direct containers. Note that if there's both a sub-surface and a sub-container, the method will return everything that's in either of these; and likewise with the sub-rear-surface and sub-underside.

    This change is designed primarily to improve the behavior for ComplexContainers, which wasn't ideal under the old mechanism, as it only returned the complex container's sub-components, which are internal implementation objects that are meant to be invisible to the player. Clearly, in the case a complex container, it's the contents of the inner containers that are of interest in TAKE ALL FROM. This change also makes it easier to customize the TAKE ALL FROM behavior for your own special containers, since it gives the container a chance to determine what ALL means in context.

    The grammar class VagueContainerNounPhraseProd, which matches locational phrases such as "all in box" and "the one in the box," also uses the new method to determine which objects are involved. This provides the same improvement for commands such as LOOK AT EVERYTHING IN THE BOX when a complex container or other special container is involved.

    The NounPhraseWithVocab method getVocabMatchList() now takes an extra parameter, specifying any extra flags that should be OR'd into the ResolveInfo flags for the items in the match list. In the past, the version of the method in the adjective-phrase subclasses took the extra flags parameter, but the base class method didn't, which made it difficult to create classes for production types that could define grammars with either the adjective or noun subproductions.
    In the past, if the player typed STAND while the PC was sitting or lying on an object derived from BasicPlatform, the library simply made the PC stand up, remaining on the platform. In particular, the library didn't bother checking the allowedPostures property to see if the platform actually allowed the 'standing' posture; it simply assumed that standing was allowed on any platform. This didn't easily allow for things like booths with limited headroom. The makeStandingUp method of BasicPlatform now checks allowedPostures property, to make sure that 'standing' is listed. If 'standing' isn't among the allowed postures, the makeStandingUp method now inherits the base class handling, which removes the actor from the nested room and sets the actor's posture to the default for the enclosing location. This will produce better results in most cases, since the natural implication of STAND UP when in a vertically confined space would be to remove oneself from the space.
    It's now possible to control the order in which preconditions are executed, at the level of the individual preconditions. The new PreCondition property preCondOrder gives the order of execution of a given precondition. The default for most types of preconditions is 100; for TouchObjCondition (and thus for touchObj), it's 200, which means that touchObj conditions will execute after other conditions by default.

    It's occasionally useful to be able to fine-tune precondition order because preconditions can sometimes interfere with one another. This is particularly a problem with implied actions that require the actor to be in a particular posture or location, because many of the standard preconditions can move the actor around as a side effect. For example, a doorOpen condition could execute an implied STAND as part of its implied OPEN DOOR; if another precondition requires the actor being to be sitting, its effect would be undone if the doorOpen were executed second. We can safely STAND and OPEN DOOR first, then SIT DOWN second, because we'll still satisfy the doorOpen condition even though we're no longer standing when we're done.

    In addition to the new preCondOrder ordering, the default ordering has changed for two-object verbs (those with direct and indirect objects). By default, the direct object preconditions are now executed first, followed by the indirect preconditions; in the past this order was reversed. This is only the default order - the preCondOrder takes precedence. That is, the action builds a list consisting of the direct object preconditions followed by the indirect object preconditions, then sorts that list in ascending order of preCondOrder, preserving the existing relative order of objects with the same preCondOrder.

    adv3.h now defines a couple of new templates for your convenience:

       SyncEventList template ->masterObject inherited;
       Hint template 'hintText' [referencedGoals]?
    
    When an Unthing is used as a possessive or locational qualifier ("the key in the unthing" or "the unthing's key," for example), the parser now shows the Unthing's "not here" message. In the past, the parser showed the same default messages for an Unthing as for any other object, using wording like "The unthing doesn't appear to have that" or "You see no key in the unthing." This wording was incongruous for Unthings because an Unthing represents the absence of an object, and hence an Unthing can't "appear" to have or contain - or even not have or not contain - anything. Showing the Unthing's "not here" message yields more sensible results.
    An Unthing now responds to LISTEN and SMELL commands with the same message it uses for EXAMINE (by default, "You don't see that here"). This applies even if the Unthing is distant or obscured. (In the past, the inherited Decoration messages for these verbs were used.)
    The English library provides a new template for Unthing:

    Unthing template 'vocabWords' 'name' @location? 'notHereMsg'?;
    
    The finishGame() function now updates the global turn counter to reflect any turn currently in progress, or, when called from a daemon or fuse, the turn the player has already completed. In the past, the turn counter displayed in finishGame() didn't reflect the turn in progress that triggered the call to finishGame(). Internally, a turn isn't counted in the global turn counter until it's finished; but from the player's perspective, a turn should count as soon as it starts. This change ensures that the count that finishGame() displays is consistent with the player's expectations.
    A Lockable that's also an Openable no longer reports its locked/unlocked status when the object is open. By default, an Openable must be closed in order to be locked, so if it's open, it's always unlocked. This means that the locked/unlocked status is effectively redundant information when the object is open.

    By way of implementation, the Lockable class has a new method, lockStatusReportable, that indicates whether or not the lock status is worth mentioning in the object's current state. (This doesn't supersede lockStatusObvious, but rather complements it. lockStatusObvious still indicates whether or not the lock status is visually apparent; lockStatusReportable indicates whether it's worth mentioning from an aesthetic point of view.) Lockable defers to any mix-in class that provides the method, and Openable takes advantage of this by providing an implementation of the method that returns true if the object is closed, nil if it's open.

    Door and SecretDoor are now based on a new common base class, BasicDoor. The new BasicDoor class defines the core internal behavior of a door: a travel connector object that can be open or closed, that allows travel only when open, and that can have two objects linked together to represent the two sides of the door.

    The relationship between BasicDoor and Door is analogous to that between BasicOpenable and Openable. BasicDoor defines the internal behavior, while Door adds handling for user commands to manipulate the door (in particular, OPEN, CLOSE, LOCK, UNLOCK).

    This change doesn't affect the functionality of Door; it merely rearranges the code for Door internally by moving a few methods to the new BasicDoor base class. SecretDoor isn't much affected, either, but does now add support for remembering a recent traversal for disambiguation purposes, as Door did in the past (and still does). No changes should be needed in existing game code.

    The main benefits of this change are that it makes SecretDoor behave more consistently with the Door class, and it provides a single base class for all door-like objects. In addition, the new BasicDoor class makes it easier to define custom doors with special controls, since it has all of the necessary functionality but doesn't make any assumptions about handling commands.

    BasicDoor.noteRemoteOpen() now calls a new method, describeRemoteOpen(), to generate the message describing the change. This makes it easier to override the message for a door, since you can simply display the custom message without having to worry about the sense context.
    The Enterable and Exitable classes no longer accept the GO THROUGH command. In the past, these classes did accept GO THROUGH as synonymous with travel, but this became undesirable when the EntryPortal and ExitPortal classes were added. Enterable and Exitable are meant for things like buildings, where ENTER or EXIT make sense but not GO THROUGH; EntryPortal and ExitPortal are for things like doors and passages, where you can equally well ENTER/EXIT them or GO THROUGH them.
    The AutoClosingDoor class has a new method, reportAutoClose(), that generates the message that mentions that the door closed behind whoever just went through it. The TravelVia action() handler calls reportAutoClose() after it automatically closes the door; by default, reportAutoClose() shows the standard library message &doorClosesBehindMsg. In the past, the action() handler simply showed the same library message itself, rather than calling out to a separate method.

    The purpose of this change is to make it easier to customize how the auto-closing is reported, or whether it's reported at all. If you want the door to close silently, without any mention that it closed, simply override reportAutoClose() to do nothing.

    By default, a FireSource object now removes itself from consideration as a default indirect object for the BurnWith action if it's not already lit. This ensures that the library won't (for example) attempt to use an unlit candle as a default source of fire to light another candle. In the past, under certain circumstances, the library could cause a stack overflow by trying to light one candle with another, which it tried to light with the first, and so on. This change ensures that a fire source will only be chosen as the default indirect object for BurnWith if it's already lit.

    (Matchstick objects, of course, aren't affected by this change, since they can light themselves on fire independently. Matchsticks thus remain valid as default choices for BurnWith even when they're unlit.)

    The Wearable class now sets an elevated likelihood (via logicalRank()) for a REMOVE (or TAKE OFF or DOFF) command when the object is being worn. This helps avoid disambiguation prompts for these commands.
    When a Hidden object is discovered (via its 'discover()' method), the object now marks itself and its contents as having been seen by the player character, assuming they're within sight at the time the object is discovered.
    The new RoomPart methods moveIntoAdd(room) and moveOutOf(room) make it easier to dynamically change a room's set of RoomPart objects. In the past, there was no straightforward way to change a room's RoomParts dynamically; these new methods make it easy by making the necessary updates to the affected room's properties.
    Room.getExtraScopeItems() now adds the room's floor to scope only if the actor is directly in the room. In the past, this routine added the floor to scope unconditionally. The rationale of adding the floor to scope is that an actor standing in a room is aware that there's a floor there simply by virtue of standing on it - but that rationale only holds when the actor is actually standing on the floor (or sitting on it, or whatever), and that's only the case when the actor is actually directly in the room. If the actor is in a connected room, or inside a nested room within the outer room, then in most cases the actor isn't actually in contact with the floor.

    Similarly, NestedRoom.getExtraScopeItems() now adds the nested room to scope only when the actor is directly in the nested room. The rationale is the same as for the floor in an outer room.

    The TALK TO command now treats talking to oneself as illogical; in the past, it was merely considered improbable.

    A side effect of this change is that the intransitive conversational commands (HELLO, GOODBYE, YES, NO) won't try to make the PC talk to itself by default. In version 3.0.8, these commands were enhanced to choose the same default interlocutor as TALK TO would when the most recent interlocutor is no longer present. This had the undesirable effect of choosing the PC as the default target when no other actors were present. Now that TALK TO won't choose the PC as the default actor, HELLO and the rest won't do so, either.

    In past versions, the InConversationState's "boredom counter" was handled inconsistently in certain cases. In particular, when an NPC initiated a conversation, it took one more turn for the NPC to become bored than it took when the PC initiated the conversation. This has been corrected.
    The library now uses the "identity" object when setting a pronoun antecedent in Actor.setPronounObj(). This ensures that when an internal program object is being manipulated, and that object generates a pronoun antecedent, any subsequent command reference to the antecedent will properly refer to the main simulation object associated with the internal implementation object. For example, this ensures that when a ComplexComponent is manipulated, subsequent references to "it" refer back to its ComplexContainer parent.
    A library bug introduced in version 3.0.8 caused a run-time error when the player attempted to address a command to another actor and used a pronoun to identify the target actor (as in HIM, GO NORTH or ASK HIM TO GO NORTH - the first phrasing is awkward enough that most players wouldn't think to use it, but the ASK phrasing is natural enough that players might try it). This has been corrected.
    A bug in ThingOrTopicMatchTopic.isMatchPossible caused a run-time error under certain conditions. This has been fixed.
    The <.roomname> style tag now adds a newline after the room name text; this is in lieu of the <.roomdesc> style tag showing a newline before the room description, as was done in the past. The newline is really part of the room name formatting, to ensure that the room name goes on a line by itself. This change won't affect most existing games, since the usual LOOK AROUND format shows the room name followed by the long description; but the change does ensure that a room description won't have an extra leading newline in cases where the room name isn't displayed.
    In Intangible, Fixture, Decoration, Room, and RoomPart, hideFromDefault() now simply returns nil. (In the past, these classes defined hideFromDefault() as returning the inherited value, which didn't have the desired effect of bypassing the inherited call (from Thing.hideFromDefault()) to self.hideFromAll(). These classes override hideFromAll(), but they want to use separate handling for hideFromDefault(). The old override didn't correctly accomplish this because of the way the base Thing implementation of hideFromDefault() is written.)
    The library now responds to commands like THROW ME, THROW ME AT something, and THROW ME TO something with an appropriate failure message. (In the past, these commands were actually allowed, and used the standard THROW handling, which obviously isn't desirable.)
    The library now accepts SIT and LIE (and, equivalently, SIT DOWN and LIE DOWN) as intransitive verbs. In the past, these were defined as missing-direct-object forms of the transitive commands SIT ON and LIE ON, which meant that the library tried to find a default direct object. This had the undesirable effect of making an actor who was already sitting/lying automatically stand up and sit/lie on some other suitable object in the room, which as a last resort was the default floor. The new intransitive verbs check to see if the actor is already sitting/lying, and if so, simply generate an error message to this effect. If the actor isn't already sitting/lying, the new commands proceed as before: they look for a suitable default object, and sit/lie on that.

    As part of this change, the intransitive SIT DOWN and LIE DOWN phrasings no longer have [badness], so the parser will match them as full-fledged intransitive phrasings.

    There are two small changes to the action message processor.

    First, it's now possible to define a message method on a Thing that takes no arguments, even if the message normally does require arguments. This makes it more syntactically convenient to define simple message string or property-redirect methods that require some small chunk of code, but which don't need any of the argument values that are normally sent to the message method. For example, we can now write this:

       notImportantMsg = (myGenericMsg)
    

    instead of this (which was formerly required):

       notImportantMsg(obj) { return myGenericMsg; }
    

    Second, these sorts of message methods can now return nil to indicate that they don't want to provide the message after all. This is useful when an object only wants to override a library message when it's in a certain state. It's also useful when an object wants to provide the message only when that object is in a particular role in a two-object command (i.e., a command with both a direct and indirect object). Recall that when a command involves both a direct and an indirect object, the message processor asks both of the objects for an override; this new capability lets the objects be selective about providing an override according to their actual roles in the command.

    For example, suppose you want your 'vase' object to override the "not a container" message for PUT IN, but only when the vase is the indirect object of the PUT IN, not when it's being put somewhere itself. To do this, you could write vase.notAContainer like so:

       notAContainerMsg = (gIobj == self
                           ? 'The vase\'s opening is too small.' : nil)
    

    The library provides a pair of new macros that simplifies the syntax for that kind of conditional message override: dobjMsg() and iobjMsg(). These macros simply test to see if gDobj or gIobj (respectively) equal 'self'; if so, they return the argument, and if not they return nil.

    The statusLine object has a new method, setColorScheme(), that sets up the status line's colors. This change won't affect any existing code, since it's just a minor internal reorganization, and the behavior is the same as it used to be. The point of the change is that it's now easier to customize the status line color scheme, since you only have to override this one method, and the only thing the override has to do is to write out a <BODY> tag with the desired color scheme.
    The English tokenizer now recognizes upper-case "'S" (apostrophe-S) suffixes as equivalent to the lower-case version. In the past, the tokenizer only recognized this token in lower-case.
    In the English version, the SpecialTopic matcher now specifically rejects matches for several "weak" topic command inputs: I, L, and LOOK. The matcher only rejects the matches when these are the only thing the player typed. If the player types just "I", for example, they probably intend to enter an INVENTORY command rather than to select a special topic that happens to have "I" among its tokens (as in "tell him I don't know").
    In the English grammar, plurals are now accepted as possessive qualifier phrases (as in "the women's books"). When a plural possessive is used to qualify a plural phrase ("the women's books"), the parser resolves the overall phrase to all of the in-scope objects matching the base noun phrase that are owned by anyone matching the qualifier phrase. When a plural possessive qualifies a singular noun ("the women's book"), the parser makes the same initial selection, but then prompts for more information if the selection includes more than one object.
    When a possessive phrase is used to qualify a noun phrase, and the result is ambiguous, the parser now filters the list of possible matches using the filterResolveList() mechanism. This ensures that special objects - especially Unthings and Collectives - are treated the same in this case as they are in other situations. In the past, for example, the parser included Unthings in a disambiguation list if the Unthings matched the vocabulary of a possessive qualifier; this was undesirable because Unthings aren't meant to be treated as actual, present objects from the player character's perspective.
    In the past, if the player responded to a disambiguation prompt with a possessive phrase, and the specified possessor didn't own any of the objects in the original disambiguation list, the parser responded with a message like "Bob does not appear to have any such thing." This was inconsistent with the way disambiguation responses are treated for other types of responses. Now, an inappropriate possessive response receives the same message as would a random adjective that doesn't apply to any of the original possibilities: "That was not one of the choices."
    The English version of the library now lets the Action determine which message to use when there's nothing in scope matching a noun phrase in a command. In the past, the parser always used the "noMatch" message, the default wording of which was "You see no noun phrase here." This was sometimes jarring for commands that generally refer to non-visual objects, such as LISTEN TO or SMELL. With this change, the library now calls the Action (via its new method noMatch()) to determine which message to show. Most actions simply invoke the new message method noMatchCannotSee(), which shows the same message as before. The LISTEN and SMELL actions override noMatch() to call the new message method noMatchNowAware(), which shows the message "You are not aware of any noun phrase here." The new wording should be more natural when the noun phrase is something inherently non-visual.

    (It would be even better if the library could decide based on the noun phrase itself whether the noun phrase refers to something visual or not, since the player could just as well try to EXAMINE THE SULFUROUS ODOR or TAKE THE BEEPING NOISE. Unfortunately, it's effectively impossible to do this, since the true meaning of the error message is that the parser can't determine what the noun phrase actually refers to. We don't want to say that outright, of course, since that would detract from the player's sense of immersion in the game world by calling attention to the parser and its limitations, so the best we can do is to find a heuristic that works most of the time. The addition of the per-action customization improves on the old scheme and should make incongruous cases relatively rare.)

    The parser's algorithm for choosing grammar matches has been fine-tuned to handle cases with truncated words and adjective-ending phrases a little better, particularly in commands involving multiple objects. In some cases, when presented with a two-object command (e.g., PUT X IN Y) in which one of the object phrases failed to match any in-scope objects, for the other phrase, the parser formerly chose a noun-ending phrase over an adjective-ending phrase, even if the adjective-ending version matched an object in scope and the noun-ending version didn't. This sometimes yielded odd "you don't see that" messages that named the object that actually was in scope, from the player's perspective. This change should improve the behavior in situations like this.
    In the English library, the code that tries to figure out whether to use "a" and "an" for an object's aName now ignores any leading HTML tags in the object's base name. This is useful for objects with aName strings such as "<i>Apple County Times</i>".

    In addition, the code now specially recognizes names that start with one-letter words (such as "X-ray" or "<q>L</q> button"), and assumes they're pronounced by saying the name of the letter. So, for example, "X-ray" turns into "an X-ray," since the "X" is pronounced as "ex."

    The English library now accepts SET as a synonym for PUT in the commands PUT DOWN, PUT ON, PUT IN, PUT UNDER, and PUT BEHIND.

    SET is also now accepted as a single-object verb. When applied to a Thing, the library assumes the intention is SET thing ON something, and prompts for the other object. When applied to a Settable, the library assumes the intention is SET settable TO setting, and prompts for the setting.

    Note that the single-object form (simply SET dobj) could be useful all by itself in some cases: SET ALARM, for example. For such cases, you can simply define a custom dobjFor(Set) for the object.

    In the English library, L is now accepted as a synonym for LOOK in the various forms of the LOOK UP and LOOK FOR commands: L UP topic IN object, L topic UP IN object, L FOR topic, etc.
    In the English library, THROW iobj dobj is now accepted as a synonym for THROW dobj TO iobj, allowing phrasing like THROW BOB THE BALL.

    In addition, THROW obj direction (as in THROW BOOK EAST) is now accepted, via the new action ThrowDir. The default handling for ThrowDir (in Thing) is simply to display a message essentially explaining that the proper command is THROW AT: "You need to be more specific about what you want to throw the book at." If the phrasing is THROW obj DOWN or THROW DOWN obj, though, we use the same response we use for THROW AT FLOOR ("You should just put it down instead").

    The parser's grammar ranking mechanism has a new ranking criterion: "phrasing weakness." This is an arbitrary numeric value that the language module can use to rank particular phrasings as relatively weak, which means that they're less likely to be valid matches.

    The English grammar uses this new criterion to mark phrasings as weak when they involve two object phrases with no prepositional marker and no qualifier word - things like GIVE BOB BOOK. These phrasings are weak because they could be interpreted as a single object with a missing phrase instead of two objects - if the wording were GIVE GREEN BOOK, for example, there's probably a GREEN BOOK and a missing object rather than two objects GREEN and BOOK. A phrasing such as GIVE BOOK TO BOB isn't weak because the prespositional marker makes the grammar fairly unambiguous; likewise, GIVE BOB THE BOOK is relatively clear because the article "the" can't normally go in the middle of a noun phrase, so most likely introduces a second noun phrase.

    In the English library, CLIMB UP and CLIMB DOWN without a direct object are now explicitly recognized in the grammar as missing-object versions of CLIMB UP dobj and CLIMB DOWN dobj. This prevents the CLIMB dobj grammar from treating the UP or DOWN as a direct object, which usually yielded an ungainly response along the lines of "you see no up here." (The missing-object CLIMB UP and CLIMB DOWN are marked with "badness" in the grammar, which means that if there actually is an in-scope object with vocabulary words "up" or "down", the CLIMB dobj form will still be matched - so CLIMB UP as short-hand for CLIMB THE UP STAIRWAY will continue to work as it did before.)
    In the English library, the message substitution parameter "{a/he}" was incorrectly not marked as a sentence subject. It's now so marked.
    In the English library, the methods askMissingObject(), missingObject(), askMissingLiteral(), and missingLiteral() have been moved from class messageHelper (in en_us.t) to playerMessages (in msg_neu.t). These methods are just message generators, so they belong in the message object with all of the other message generators.
    In the English library, the messages for noMatchForAll have been changed to variations of "You see nothing suitable." In the past, the message was "You see nothing to use for 'all' here," which was somewhat misleading: it suggested that there's nothing present at all, when the actual point is merely that there's nothing present that can be used in the manner specified.
    In 3.0.8, OOPS X (for any literal X) was changed so that it didn't consume a turn, but OOPS (without a literal) was inadvertantly left unchanged, so it still counted as a turn. This has been corrected: a bare OOPS command no longer consumes a turn.
    In the past, if the player typed NOTE without any text for the note, the parser prompted for the missing text ("What do you want to note?"). It no longer does this. Instead, the first time an empty NOTE is entered, the parser displays a message explaining that NOTE is usually used to enter a message, in case the player wasn't aware of that; and after that, the parser simply accepts an empty NOTE the same as any other.
    In the past, typing the secret command XSPCLTOPIC by itself on a command line caused a run-time error. (XSPCLTOPIC is a verb that the library uses internally to handle SpecialTopic entries. It's not something the player ever needs to type, or indeed is ever allowed to type, but at certain time, the library internally runs XSPCLTOPIC commands through the normal command processing as though the player had typed.) This has been fixed.
    In the past, if the parser asked for a missing literal phrase (such as for TYPE), and the player responded with input that included a character that the game's tokenizer didn't accept, the game displayed an "unknown exception" message. The problem was that the parser didn't properly catch the tokenizer's bad-token exception in this case. The parser now catches this exception and reports it as normal.
    In the English library, when the parser is announcing the name of each object in a multi-object command, paragraph breaks are now suppressed after the object name. This slightly improves formatting in cases where the action response for an object happens to start with a paragraph break.
    In the English library, the verb-phrase builder method (getVerbPhrase()) for TopicAction, TopicTAction, LiteralAction, and LiteralTAction didn't work correctly. This has been corrected.
    The new output control pseudo-tag <./P0> cancels the effect of an immediately preceding <.P0> tag. This is useful in certain cases where you want to ensure that a newline or paragraph break is displayed, even if a <.P0> was just displayed.
    All of the various spellInt-related functions are now implemented in en_us.t. Some of these functions were formerly in numbers.t, but that wasn't the right place for them because some language modules might need to change these functions' interfaces (for example, to specify the grammatical case or gender of the result). This change should have no effect on any existing game code; it's a purely internal change.
    The message property 'flashlightOnButDark' has been renamed to 'flashlightOnButDarkMsg' (that is, the suffix 'Msg' has been added) for consistency with the other message property names.
    The default library message for TASTE has been changed to "It tastes as you expected" (from "You taste nothing out of the ordinary"). This emphasizes that the physical action was completed but that nothing unusual was detected; the old message could have been read as suggesting that the object in question had no flavor at all, which obviously isn't true of most objects.

    In addition, UntakeableActor and Person now use messages similar to those used for TAKE ("The cat won't let you," "Bob probably wouldn't like that"), since these sorts of actors are meant to exhibit volition, and in the real world would generally not allow themselves to be arbitrarily tasted.

    A couple of the default library messages for POUR incorrectly referred to the direct object where they meant to use the indirect object. These have been fixed.
    3.0.8

    Released 9/12/2004

    Compatibility-breaking change: The class formerly named ShipboardRoom has been renamed to simply Shipboard. This name change is for consistency; most of the other similar mix-in classes are named as simple adjectives. You should search your game code for any mentions of ShipboardRoom and change each to Shipboard.

    Note that the new class ShipboardRoom is provided for convenience as a pre-defined combination of Shipboard and Room. This means that any rooms you previously defined with the superclass list ShipboardRoom, Room (and just changed, following the prescription above, to Shipboard, Room) can now be written simply as ShipboardRoom. This is purely for convenience; you can leave the class list as Shipboard, Room and it will behave the same way.

    Compatibility-breaking change: The class formerly named OccludingConnector is now called Occluder. This change emphasizes that the class is a mix-in that isn't limited to use with sense connectors.
    Possibly compatibility-breaking change: The parameters to the enterConversation() method of the ConversationReadyState class have changed. The old second parameter, 'explicit', has been deleted and replaced with a new parameter, 'entry', which gives the TopicEntry (if any) that triggered the conversation entry. Any game code that calls or overrides enterConversation() must be changed to accomodate the parameter change.

    The new parameter gives enterConversation() more information about what triggered the conversation. This extra information is necessary because, without it, the conversation system wasn't able to properly handle topic entries that were marked as conversational but non-greeting (that is, the isConversational property is true, but impliesGreeting is nil). In the past, such entries were incorrectly treated as though they were entirely non-conversational, so they didn't enter an in-conversation state when used in a response.

    Note that the same information conveyed by the old 'explicit' parameter can be inferred from the new 'entry' parameter. When 'entry' is nil, it means that the conversation entry is explicit, because it was triggered by a direct action (HELLO, etc.) rather than a TopicEntry. When 'entry' is non-nil, it means that the conversation entry is implied by the TopicEntry.

    Possibly compatibility-breaking change: The ConvNode method processSpecialCmd() has an additional parameter, as does the SpecialTopic method matchPreParse(). It's unlikely that any game code will be affected by this, because games don't normally override these methods - usually, games just define SpecialTopic keyword lists.

    The new parameter in both cases gives the "processed" version of the player's input. The default SpecialTopic implementation now matches the processed version of the input rather than the original text the player entered.

    In the English library, the processed input is the player's original input with any periods, commas, question marks, exclamation marks, colons, and semicolons removed, and with any leading "A" or "T" keyword removed.

    The purpose of the change to the parameters of these methods is that it makes it simpler and more efficient to do this filtering.

    Note that the punctuation filtering is new. We filter out the punctuation because it's not usually meaningful for matching special topics. For example, if the player's input contains the correct keywords to match a special topic, we usually want to match the special topic even if the player (for example) put a period or question mark at the end of the command.

    Possibly compatibility-breaking change: The format of the message generated by DropType.getReportPrefix() has been changed so that the message is expected to be a complete sentence. This affects the library messages floorlessDropMsg, droppingObjMsg, throwHitMsg, and throwShortMsg (this last one is new in this update). These methods and messages are mostly for internal use, but if you override or call DropType.getReportPrefix() in your code, or if you've customized any of these messages, you'll need to adjust your own messages for this change.

    The old sentence fragment format had a couple of problems. First, it was likely to be problematic for some non-English translations. Second, it was less flexible than the new full-sentence format, even in English, for the purposes of adding new "drop type" objects and of adding new messages composed using the prefix.

    The parser's pronoun antecedent rules have changed slightly. In the past, when the player entered a command with multiple noun slots (such as UNLOCK DOOR WITH KEY), the parser always chose the last-resolved object as the antecedent for any future pronoun. This meant that the parser remembered either the direct object or the indirect object, depending on the verb. This rule didn't always produce the results that you'd expect in natural English conversation, and had the further drawback that the remembered object varied from verb to verb.

    The parser now remembers both objects for two-object verbs, and waits to make a decision until the player actually enters a command with a pronoun. When the player types a singular pronoun (it, he, she), and the most recent command had multiple noun slots, the parser performs the standard "verify" evaluation on each potential antecedent and picks the most logical one. If more than one antecedent is equally logical, the parser simply picks one arbitrarily, and announces the choice in the usual manner. When the choice is completely arbitrary, it will always be the direct object of the two-noun command.

    Some examples:

       >LOCK DOOR WITH KEY
       Locked.
    
       >DROP IT
       Dropped.   (the key, because it's illogical to drop the door)
    
       >UNLOCK DOOR WITH KEY
       (first taking the key)
       Unlocked.
    
       >OPEN IT
       Opened.    (the door, because it's illogical to open the key)
    
       >LOCK DOOR WITH KEY
       (first closing the door)
       Locked.
    
       >EXAMINE IT
       (the door)
       It's a heavy wooden door.
    

    In the last command - EXAMINE IT - the choice of the two potential antecedents is arbitrary, because it's equally logical to examine the key or the door. The parser thus chooses the direct object of the prior command, and announces the choice to alert the player (in case she was thinking "it" would refer to the key).

    The new Thing method notifyMoveInto(newContainer) complements the existing moveInto notification routines notifyRemove() and notifyInsert(). The new notifyMoveInto() method is called last in the sequence, after notifyRemove() has been called on the old container tree and notifyInsert() on the new container tree. notifyInsert() gives the object being moved a chance to perform any special additional processing before the move, or to veto the move (which it can do by displaying an explanatory message and calling 'exit', just as notifyRemove() and notifyInsert() are allowed to do).

    The new routine has several benefits. First, it provides the object being moved with the same sort of notifications that the old and new containers already receive. Since it runs along with these other notifications, it allows the same distinction as those other notifiers between moves that trigger these side effects (via calls to moveInto) and those that don't (via direct calls to baseMoveInto) Second, since the new method runs last, after the notifyRemove() and notifyInsert() notifications, it has the last chance to veto the move; this means that if it doesn't cancel the move, then the move will definitely proceed (at least to the extent that baseMoveInto() will be called). Any side effects that must be carried out only when the move will succeed can thus be safely coded here.

    The parser now provides more helpful error feedback when the player tries to enter a SpecialTopic command that's not active.

    First, the library automatically enters all of the keywords defined by special topics into the game's dictionary. This ensures that the parser won't claim that special topic keywords are unknown if they're used out of context, as it did in the past. It was confusing when the parser claimed that special topic keywords were unknown, because the parser most certainly did know them some of the time.

    Second, when the player enters a command that looks syntactically invalid, but which doesn't contain any unknown words, the parser scans recent special topics for a match. If it finds a match, the parser will show a new message: "That command can't be used right now." This old response - "The story doesn't understand that command" - was potentially confusing, since the story can recognize the exact same command at other times.

    The parser scans the list of special topics most recently listed in topic inventories, using the new specialTopicHistory object. This object keeps track of a configurable number of the most recent SpecialTopic suggestions listed; set its maxEntries property, which defaults to 20, to the maximum number of recent entries to scan. The purpose of the limit is to ensure that a scan won't take too long, by limiting the scan to the specified maximum number of SpecialTopic items. The trade-off is that the parser will forget older suggestions, so they'll be met with the old "story doesn't understand" message if the player tries them. However, it seems safe to assume that the player will also forget about older suggestions, and so won't think to try them.

    If you set maxEntries to nil, it essentially removes the limit to the number of entries to scan. The library won't maintain the history list at all in this case. Instead, to check an unrecognized command, the parser will simply scan all SpecialTopic objects in the entire game, whether or not they've ever been suggested in topic inventory displays. This option might be preferable for a game where the total number of items is small enough that scanning them all doesn't take an appreciable amount of time. (Or not. Some authors might not want the parser to recognize previously-unseen special topics at all - even to the extent of saying "that command can't be used right now" - because doing so could conceivably give away upcoming plot developments. That is, if the player accidentally types a command that happens to match a special topic that hasn't ever been suggested, the player could infer from the special error message that the command will do something later in the game. The chances of actually giving away anything important this way seem rather remote, but this sort of thing rubs some authors the wrong way. If you don't like the idea of using the different error message for special topics until after they've been suggested at least once, but you also don't want a limit on the number of past topics to remember, just set maxEntries to a very high number.)

    The new property pluralOrder lets you control the order in which a set of objects is processed when the objects match a plural phrase in a command. This property applies to all VocabObjects (which is the base class for any object with vocabulary words); the arbitrary default value is 100.

    In most cases, the order of processing for the matches for a plural phrase is arbitrary, so you won't need to worry about this new property. Once in a while, though, it's convenient to be able to control the order of matching. This is especially useful when objects have ordinal names (first door, second door, etc), or when objects are named by relative spatial position (top drawer, middle drawer, bottom drawer).

    Note that the new property only controls the order of processing within an individual plural phrase. For example, in the command TAKE BOOKS AND MAGAZINES, the books will be sorted by their relative pluralOrder values, then the magazines will be sorted by theirs - but when the command is processed, all of the books will be taken before any of the magazines, regardless of the relative pluralOrder values. This is important because it ensures that commands are processed in the order specified by the player.

    Because pluralOrder and the existing disambigPromptOrder will probably be used in the same way most of the time, disambigPromptOrder now returns the pluralOrder value by default. So, for the common case that the two properties should have the same value, you can simply set pluralOrder.

    The new function cls() clears the screen, and takes care of a couple of chores that should always be done at the same time. First, the function clears any text captured in the transcript manager, ensuring that text that was meant to be displayed before the screen was cleared won't show up on the new, blank screen. Second, the function calls the new gameMain.setAboutBox() method after clearing the screen, ensuring that any <ABOUTBOX> tag is re-displayed; this is necessary because HTML TADS discards any old <ABOUTBOX> information whenever the screen is cleared.
    The new method setAboutBox() has been added to the gameMain() object. This new method is designed as the place to put your game's <ABOUTBOX> tag, if you provide one. The method is invoked during start-up, and is also called from the new cls() function, ensuring that the about-box information will be re-displayed each time the screen is cleared.

    The default implementation in GameMainDef does nothing. The new method is simply a placeholder for the game's (optional) use.

    This change is designed to make it easier to manage your about-box information. HTML TADS discards any previous <ABOUTBOX> tag when the screen is cleared, so it's necessary to re-display the tag whenever you clear the screen. If you write your <ABOUTBOX> tag in the new gameMain.setAboutBox() method, and you always use the new cls() function to clear the screen (rather than calling the low-level clearScreen() function directly), the library will automatically keep yout about-box active.

    You can now control whether or not a given verb accepts the word ALL in its noun phrases. By default, every verb accepts ALL. However, if you set the new gameMain property allVerbsAllowAll to nil, only the basic inventory-management verbs (TAKE, TAKE FROM, DROP, PUT IN, PUT ON) will accept ALL, and every other verb will simply show an error message ("'All' isn't allowed with that verb").

    Some authors like to limit usage of ALL to the basic inventory verbs, either because of tradition or because they think it's a sort of "cheating" when a player tries commands like OPEN ALL or EXAMINE ALL. This feature is probably of particular interest to writers of puzzle-oriented games.

    In addition to the all-or-(almost-)nothing option setting in gameMain, you can control ALL acceptance on a verb-by-verb basis. The new Action property actionAllowsAll controls whether a particular Action accepts ALL for its noun phrases. By default, the base definition in Action simply returns the value of gameMain.allVerbsAllowAll. In addition, the basic inventory verbs (Take, TakeFrom, Drop, PutIn, PutOn) override actionAllowsAll to return true. If you want to enable or disable ALL for an individual action, you can use "modify" to set the property for that action class.

    The library uses the new Style Tag <.announceObj> to set the display style for multi-object announcements. (These are the little announcements, of the form "blue book:", that precede the responses when a command is applied to multiple objects.)

    The default display style is to show object announcements in bold text. When a command operates on several objects, the response can get rather lengthy; the new bold-face style for the announcements is intended to make these responses easier to read by making the boundaries of the sub-parts more visually apparent.

    The new StyleTag object is called announceObjStyleTag. You can use "modify" with this object to customize the announcement style, if you want.

    Actor.lookAround(), Thing.lookAroundPov(), and Thing.lookAroundWithin() now accept a set of bit flags for the 'verbose' parameter. 'true' and 'nil' can still be passed, and have the same meanings as before, so existing code doesn't need to be changed. However, the new bit flags give you more control over the format of the listing. The flags, which can be combined with the "|" operator, are:

    Note that lookAroundWithin() automatically adds LookRoomDesc to the flags if the actor has never seen the room before.

    If you pass 'nil' for the 'verbose' parameter, the effect is the same as passing (LookRoomName | LookListSpecials | LookListPortables). If you pass 'true', the effect is the same as passing (LookRoomName | LookRoomDesc | LookListSpecials | LookListPortables).

    The main effect of this change is that it lets you generate a room description without including the room title, which wasn't possible in the past. This can be useful for cases where the standard LOOK AROUND format isn't desirable, such as when describing the view through a sense connector to another room.

    The new Thing method roomRemoteDesc(actor) lets you customize the way a room is described when it's viewed by an actor from another room. The library calls this from Thing.lookAroundWithin() when the actor who's viewing the room is not within the room being viewed.

    Note that none of the standard library commands ever call this new method, because the basic library doesn't provide any command to view a remote room. However, games might provide their own commands to do so, such as LOOK THROUGH KEYHOLE, and you might want to call lookAroundWithin() to generate the descriptions in these cases. Doing so will invoke roomRemoteDesc instead of roomDesc to generate the room description.

    The default Thing.roomRemoteDesc implementation simply calls roomDesc. You'll probably want to override this any time you actually use it, to show a suitable description from the remote actor's point of view.

    A new Thing method, adjustLookAroundTable(), lets you customize the set of objects that lookAroundWithin() can mention. By default, this routine removes the POV and actor objects (which are usually the same object) from the table, ensuring that these objects aren't mentioned.

    In the past, lookAroundWithin() simply removed the actor object directly, so the default behavior is the same as in the past. Separating this out into a new method lets games customize which objects are mentioned and which aren't for the particular room and point of view.

    The new Thing method isLookAroundCeiling() determines whether the LOOK AROUND description for a given object is generated by that object directly, or by its location. By default, this routine returns true if either the object is a top-level room (that is, it has a nil location), or the object's enclosing location isn't visible. In either of these cases, since it's not possible to see out to an enclosing location, the current object has to provide its own interior description.

    This method is called from lookAroundPov(), which formerly performed the same test directly, so the default behavior hasn't changed. This has been separated into a new method because the default condition isn't always ideal. For example, if the player's inside a closed wooden booth with a small window that looks out to the enclosing location, and the player types LOOK AROUND, we'd want to describe the interior of the booth, not the outside location. For this case, you could override isLookAroundCeiling() for the booth to return true even though the location is visible through the window.

    The new TAction method getDobjInfo() returns the full ResolveInfo object associated with the current direct object. Similarly, the new TIAction method getIobjInfo() returns the indirect object's ResolveInfo.
    The parser now keeps track of the noun phrase that was parsed for each object involved in the command. This information is kept in the ResolveInfo object, in the 'np_' property. This information can be used, for example, to obtain the original text of the noun phrase that was resolved to a given object. For example, to obtain the original text that resolved to the current direct object being processed in an action() method, you could write this:

       local txt = gAction.getDobjInfo().np_.getOrigText();
    
    Due to a bug introduced in the previous release, the method PresentLater.makePresentByKeyIf() didn't work correctly if the condition argument was a function pointer. In these cases, the routine called the function on the first object matching the key, and then used that same true/nil result for every matching object. (Thus, if the first matching object was to be revealed, every matching object was revealed; if the first was to be hidden, all were hidden.) This has been corrected; the method once again calls the given function individually for each object matching the key.
    Component objects are now hidden from EXAMINE ALL by default. Since components are simply detail features of larger objects, it's usually enough to examine the larger objects; it usually doesn't make sense to also include the components separately in EXAMINE ALL responses.
    By default, FOLLOW now reports a more helpful message when there's not enough information to carry out the command. In the past, the default message was simply "You don't know where (the actor) went." This was misleading when the real problem was that the actor's departure had been previously observed, but from some other location. The new message is of this form: "The last place you saw (the actor) was (the room where we last saw him)." Note that the library now keeps track of the last place we saw each actor, even when we didn't observe the actor leaving, to ensure that this new message is always accurate.
    The "badness" levels of some of the grammar rules in the English parser have been re-ranked, to put the various error conditions in a different order. In particular, the "miscellaneous noun phrase" rules now have a lower badness than any of the "missing noun phrase" rules. In the past, this relationship was reversed, which caused a subtle problem when a two-noun verb was used with a noun phrase containing a word not in the dictionary.

    Some background: In the English parser, certain grammar rules are defined specifically to match certain distinct patterns of erroneous user input. The point of matching invalid input is to recognize it and respond intelligently to it, rather than issue a blanket error message. Most of these invalid constructs are marked with "badness" levels, which tells the parser to match them only as a last resort, so that we match a valid grammar rule over one of these special error rules whenever possible.

    In the English grammar, each of the verbs with two noun slots (PUT X ON Y, ASK X ABOUT Y, etc.) has a corresponding special grammar rule that includes only the first noun slot (PUT X, ASK X, etc.), for cases where the player inadvertantly or intentionally types the command with only one noun. In the past, these rules weren't marked with "badness" levels. Now they are: these are treated as having "badness" equivalent to that of an empty noun phrase. This ensures that they'll be matched only as a last resort, when there isn't a more "correct" interpretation of the phrase.
    CollectiveGroup objects are now explicitly marked as unlisted in room or object contents or in inventories (that is, a CollectiveGroup's isListed, isListedInContents, and isListedInInventory properties now return nil). In the past, collective group objects incorrectly showed up in these listings as separate objects under certain circumstances, such as when their individuals were self-illuminating (brightness = 1) and the enclosing room was dark.
    In the past, the description of a dark room listed every visible self-illuminating object, even including those being carried by the actor doing the looking. Now, the description excludes objects in the actor's inventory, since these objects aren't part of the actor's surroundings.
    In the past, the INVENTORY command included in its listing any items that were being directly held, even if they weren't visible, on the assumption that the actor could identify these items by touch alone. This has now been refined slightly: instead of unconditionally including directly-held items in the listing, the library now includes directly-held items that the actor already knows about, as indicated by the actor's knowsAbout() method. The rationale is almost the same as before, but this change means that we no longer assume that the actor can identify an item by touch unless the actor already knows about the item. Of course, this change doesn't affect the listing of visible items - visible items are listed whether previously known or not.
    The "finish option" handler for RESTORE (finishOptionRestore) now terminates any remaining processing on the command line that triggered the finish options, when the RESTORE is successful. This ensures that when the finish options are triggered from somewhere in the middle of a command (such as in a beforeAction method), there will be no strange side effects from carrying out the rest of the triggering command.
    In the English grammar, LOOK OUT is now a synonym for LOOK THROUGH. This allows constructs like LOOK OUT WINDOW, which is more natural for some objects than LOOK THROUGH but has the same meaning in virtually all cases.
    In the English grammar, the acceptable phrasing for the SitOn command no longer includes "sit object" or "sit down object." These phrasings aren't grammatical, but they were included to match intransitive inputs (SIT and SIT DOWN) and then ask for the missing direct object phrase. Now, rather than matching the intransitive forms using the main SitOn rule, they're matched using the SitOnWhat rule. The same change applies to the LieOn rules.
    UnlistedProxyConnector.primaryConn is now initialized to TadsObject by default. In rare cases, game code might invoke a property of an UnlistedProxyConnector before the object is constructed; this new default (rather than nil) provides reasonable behavior in these cases, without requiring an added run-time test for a nil primaryConn.
    In the English messages, the AutoClosingDoor's announcement (of the door having automatically closed) now has a leading paragraph break, to set it off from the room description or other travel report that usually precedes the announcement.
    The English library's rules have changed slightly for substituting reflexive pronouns in message strings. In the past, the library always substituted a reflexive pronoun when an "objective-case" parameter matched the subject of the sentence; for example, in the message string "{You/he} can't eat {the dobj/him}," if the direct object was the same as the actor, the message would be "You can't eat yourself" (rather than simply "You can't eat you"). The new rule is almost the same, but has a new exception: if the parameter name of an objective-case parameter is the same as that of the subject of the sentence, the reflexive substitution is not used.

    For example, the library now displays "{You/he} close{s} the door behind {you/him}" as "You close the door behind you." The old rule would have used "behind yourself" instead, but the new exception kicks in because both the subject ("{You/he}") and the objective-case item ("{you/him}") refer to the 'actor' parameter object.

    The reason the automatic reflexive substitution exists in the first place is that it allows you to write a message string that refers to multiple parameters that might end up being the same object some of the time. In the first example ("{You/he} can't eat {the dobj/him}"), the direct object and the actor might sometimes be the same, but usually aren't; since the library automatically provides the reflexive pronoun when the direct object happens to be the same as the actor, the same message string works for any combination of objects ("You can't eat yourself" vs. "You can't eat the deck chair"). However, this rule is too aggressive without the exception. The point of the exception is to give authors exact control in cases where they know in advance that the subject and objective-case object will be the same, by virtue of the fact that the message is referring to the same parameter by name in both cases. In these cases, there's no need for the library to provide reflexive pronouns conditionally, because the author knows in advance that the objects will always be the same - so the author will know whether or not the reflexive pronoun is desired, and will write it that way, or not, as desired.

    If an exception occurs during an NPC's turn (in ActorState.takeTurn, for example), the scheduler will now count the NPC's turn as over, so that other NPC's get a chance to run before the same NPC takes another turn. In the past, an uncaught exception during an NPC's turn bypassed the turn-counting for the NPC, so the same NPC got another turn immediately; in some cases, this caused the NPC to try the same erroneous operation again, which set up an infinite loop as the erroneous NPC code was invoked over and over without giving anyone else a chance to run. The same change applies to fuses, daemons, and any other "schedulable" object.
    In the English library, SCRIPT ON is now accepted as a synonym for SCRIPT (for consistency with SCRIPT OFF), and RECORD ON as a synonym for RECORD. In addition, the RECORD ON/OFF messages have been changed to sound less geeky (they formerly referred to "RECORD mode," but now talk about "command recording" instead).
    In the English library, "inside" and "inside of" are now accepted in locational qualifier phrases (as in "read the coin inside the box").
    When the player looks behind or through a closed door, the library now assumes that the player's intention is to open the door to see what's on the other side. So, the library now applies an objOpen precondition to these commands, which will implicitly open the door if it's closed.

    In addition, in the case of LOOK BEHIND, the library uses a special message if the implicit Open was applied: "Opening the door reveals nothing unusual." This message emphasizes that we're looking through the passage, not at the space created between the door and the adjacent wall (if such a thing is applicable).

    When we look behind a door that was already open, the library assumes that the player's intention is simply to look at the back side of the door, or the space between the door and the adjacent wall. In this case, the inherited Thing message ("you see nothing unusual behind the door") is used.

    LOOK THROUGH PASSAGE now has a custom response when the passage is open: "You can't see much through (the passage) from here." In the real world, we can typically look through a passage into the next room, but the library world model doesn't actually allow us to see anything in the next room; so we don't want to say that we can't see through the passage at all, but at the same time we can't actually say we see anything. The new default suggests that we can in principle see some distance through the passage, but not far enough to see anything in the connected location.

    When a passage is closed, the inherited Thing message ("you can't see anything through (the passage)") is used instead, since a closed passage is assumed to be something opaque, such as a closed door.

    Looking through, under, or behind a room part now yields a custom response that says that you simply can't do that, rather than using the inherited Thing defaults (which say that you can't see anything there). For room parts, it doesn't generally make sense to perform any of these actions, which the new custom messages try to convey.
    If a player types simply GO or WALK, the library now displays a more helpful message: "You'll have to say which way to go." In the past, the parser didn't recognize that phrasing at all, so responded with the standard unknown-verb message.
    PathPassage now uses a custom message for STAND ON: "If you want to follow the path, just say so." The inherited default ("That isn't something you can stand on") doesn't make a lot of sense for paths, but at the same time, we don't want to treat every path passage as a separate nested room, so this message seeks to convey that the action might be logical in the real-world but isn't required in the game.
    Actor now handles a command of the form PUT ME IN container by replacing it with ENTER container. This is done any time the direct object is the target actor.

    In addition, the actor is now excluded in all cases from being the default direct object for PUT IN. In the past, the actor was selected as the default if there was nothing better, which occasionally led to odd results.

    Actor now treats the commands PUT ON and PUT UNDER as illogical when applied to oneself (as in PUT ME ON THE TABLE).
    Thing now calls verifyMoveTo() to verify a DROP command only when the object is being held by the actor performing the command. When the object isn't being held, the DROP will be illogical on that basis alone - the object definitely won't be moved, so there's no reason to verify the move. This is important because some subclasses use verifyMoveTo() to report other catch-all conditions ("that's too heavy to move," etc), which in the past sometimes were reported instead of the more basic problem ("you're not holding that"). By avoiding the call to verifyMoveTo() when the object isn't being held, we ensure that the more basic problem is always reported instead of any applicable catch-all condition.
    The Immovable class incorrectly handled certain two-object actions (such as PUT IN, PUT ON, MOVE WITH) by failing in the action() routine. This still allowed the other object involved in the command to handle the action successfully. For these two-object actions, the class needs to block the action in the check() routine instead, which it now does.
    When an AgendaItem throws an exception (including things like 'exit' signals) out of its invokeItem() method, the library now automatically marks the agenda item as finished (by setting its isDone property to true). In most cases, if an agenda item throws an exception, it's because something went wrong; since the same thing would probably go wrong again if the item were tried again, allowing the agenda item to remain in its "ready" state typically resulted in an infinite loop as the actor kept trying the same item over and over. This change avoids infinite loops in these cases by ensuring that an item that throws an error isn't retried automatically.
    When an object provides its own custom definition for a library message (via one of the xxxMsg properties), it can now be defined as a simple single-quoted string, even if the library method for the message normally takes one or more arguments. This makes it more convenient to define these custom messages. Of course, you can still provide a full method definition that takes the same arguments as the corresponding library message method.
    Settable now applies a touchObj precondition to the SetTo action. This ensures, for example, that a dial is reachable before it can be turned to a new setting.
    The Floor class now includes a touchObj precondition for StandOn, SitOn, LieOn, and (as an indirect object for) PutOn. This ensures that a floor in a connected but unreachable room (such as a room connected by distance) will be ruled out as a potential target object for these verbs, avoiding poor defaults and unnecessary disambiguation prompts.
    The touchObj precondition now considers a distant object illogical during its 'verify' phase.

    In the past, this precondition treated an object that was visible but not reachable as still potentially logical, but at reduced likelihood. The reasoning was that the obstruction might be easily removable via an implied action. For example, an object inside an openable glass case could be made touchable by opening the glass case, which could be performed as an implied action. However, in the case of distance, there's not usually any way to remove the obstruction automatically, so there's no point in considering the object to be logical, even at reduced likelihood. The precondition still does exactly this for objects that aren't at a distance, but now assumes that distance can't be resolved automatically.

    In the past, if the player used a SEARCH command on an Openable, and the object was closed, the implied OPEN action showed the newly-revealed contents of the object. The main SEARCH command that triggered the OPEN also showed the Openable's contents, so the contents were listed twice. Openable now omits the redundant contents listing for the implied OPEN when the main command is SEARCH, just as it's long done when the main command is LOOK IN.
    A "conversational" command (HELLO, GOODBYE, YES, NO) now consistently counts as a turn for the issuing actor, even when a target actor is specified (as in "BOB, HELLO"). This corrects a subtle inconsistency that occurred when a conversational command was directed to an NPC that had a pending agenda item. In the past, explicitly directing a conversational command to such an actor ("BOB, HELLO") prevented the agenda item from executing on the same turn, because specifying the target actor caused the turn to count against the NPC's turn counter rather than the PC's; but leaving out the target actor (as in saying simply "HELLO") counted the action as a PC turn, allowing the NPC to carry out its agenda item. Now, both forms of the command ("HELLO" and "BOB, HELLO") have the same turn-counting treatment, so a pending agenda item will be allowed to proceed in either case.
    The HELLO, GOODBYE, YES, and NO commands now find a suitable default actor to talk to if the target actor isn't specified (that is, if the command entered is simply something like "HELLO" rather than "BOB, HELLO"). If there's a current interlocutor, and it's still within earshot, then it's the default. Otherwise, we look for a suitable default direct object for a TALK TO command. This makes things more convenient for the player, since it allows the player to omit the target actor any time only one suitable actor is present.
    In the past, saying BYE to an actor in a conversation-ready state (that is, an actor ready for a stateful conversation, but not currently in conversation) caused a run-time error. This has been corrected.
    Non-conversational topic entries (those with the isConversational property set to nil) now leave the actor's ConvNode unchanged. Conceptually, a response that doesn't involve any conversation shouldn't affect the thread of an ongoing conversation, so it makes more sense for such topics to leave the ConvNode unchanged.
    In the past, Thing.lookAroundWithin() generated a slightly strange description when the point of view was a remote room, and the room containing the actor doing the looking was visible from the remote room. In these cases, the actors were described as being "here" ( as in "Bob is standing here"), even though the "here" in this kind of description is more properly the remote point-of-view room. The actors are now described as though they're in a remote room ("Bob is standing in the cave"), which is consistent with the perspective of the rest of the description.
    In the past, no turn was counted for giving an actor an order (BOB, GO NORTH) that was refused in the actor's obeyCommand() method. This didn't entirely make sense, since merely attempting to give an order to an actor is an in-game action, from the player's perspective. This has been changed so that giving an order that's refused counts as one turn.
    In the past, if an actor was in an accompanying-travel state, carrying the actor and then traveling to a new room caused a run-time error. The run-time error has been fixed.

    In addition, the accompanying-travel state is now ignored when the actor is being carried. When an NPC is being carried, it will naturally move along with the actor it's accompanying, just as all of the actor's other inventory items do by virtue of being carried. We therefore don't want the NPC to separately travel to the new location, because doing so makes the actor drop the NPC.

    Thing now defines the methods canSee(obj), canHear(obj), and canSmell(obj). In the past, these were defined only by Actor, not by Thing, since only actors have senses that allow perception of the physical surroundings. In some cases, though, an actor uses an inanimate device as a point of view (a television monitor hooked up to a remote camera, for example), and in these cases it's necessary to check visibility (or other senses) from one inanimate object to another. The new methods provide this capability. They can be overridden as needed; for example, a camera that operates in the infrared might want to override canSee() to calculate visibility in a custom "infraredSight" sense rather than in the basic "sight" sense.
    The NOTE command is now ignored for the purposes of the AGAIN command. For example, if you type INVENTORY, then type NOTE SOMETHING, then type AGAIN, the game will repeat the INVENTORY command. There's no point in repeating a NOTE command, and NOTE is a sort of side conversation between player and author anyway, so it makes sense for AGAIN to act like the NOTE never happened.
    The SAVE command is now ignored for the purposes of AGAIN. There's no point in repeating a SAVE command immediately, as nothing will have changed in the game state between two consecutive SAVE commands, and hence no re-saving is warranted.
    After a game is successfully restored, the memory of the previous command is forgotten, so the AGAIN command cannot be used. This ensures that there's no confusion about whether AGAIN refers back to the RESTORE command (which would be pointless, as it would just re-restore the same state), the previous command in the same session, or the last command before SAVE from the restored game.
    When a "finish options" menu is presented (with finishGame() or finishGameMsg()), and the player selects the UNDO option, the library now treats UNDO as the last command for the purposes of AGAIN. That is, if the player answers a finish-options prompt with UNDO, and then types AGAIN, the library performs another UNDO. (In the past, the game repeated the command that led to the finish-options prompt, since that was the last actual command. However, most players wouldn't think to make this distinction, and wouldn't want to repeat whatever command led to the game-ending prompt anyway, so the most reasonable interpretation of an immediate AGAIN is to perform another UNDO.)
    The RESTORE command is now ignored for the purposes of the UNDO command. When a RESTORE succeeds, there's no way to undo anything: the restore itself can't be undone, and the incoming restored state initially doesn't have any undo history. When the RESTORE fails, it won't make any changes to the game state, so there's nothing about the RESTORE itself to undo. The main effect of this change is that typing UNDO after a failed RESTORE undoes the last undoable command immediately before the RESTORE.
    In the past, if the player typed an OOPS command at the wrong time (when there wasn't any spelling error to correct), it was counted as a turn. This was undesirable because OOPS is a meta command, not an in-game action. The command no longer counts as a turn.
    In the English library, a string pre-parser now wraps the comment text of a NOTE command in quotation marks, if the text contains any punctuation marks or the word THEN. This allows players to enter arbitrary free-form text in NOTE remarks without any danger of confusing the parser.
    The formatting in the English instructions has been tweaked slightly to make it more consistent. In addition, the parsing of the responses to the INSTRUCTIONS prompt had a couple of small problems that have now been corrected.
    A bug in the English library caused message substitution strings of form "{it's dobj/he's}" (that is, with an apostrophe-S on each example word, and the parameter name written in the middle of the two example words) to be expanded incorrectly. This has been corrected.
    In the English library, the methods (theNamePossNoun, theNamePossAdj) that build the default possessive name based on the base name of an object now add an apostrophe-S to a plural name that doesn't end in "s". In the past, a bare apostrophe was always added to a plural name, which produced the wrong results for certain words (the men', the children', the fish').
    The English library's method that builds the default plural name of an object (pluralNameFrom) now handles abbreviations more in keeping with conventional English usage:
    The new Thing property objInPrep can be used to specify the preposition to use when describing another object as being within a given object. In the English library, the default for Thing is 'in'. Note that the existing property actorInPrep is now defined by default as returning the same value as the new objInPrep, since for most purposes they have the same meaning.
    A new message is displayed when a Throw command fails because a DistanceConnector is in the way. In the past, the library used the default Thing message, which describes the projectile as bouncing off the object that's in the way. In the case of a distance connector, this doesn't make any sense. The library now uses a custom message that describes the projectile as falling short of the target.
    In the past, if an object was thrown through a MultiLoc connector to a separate top-level room, the object sometimes incorrectly landed on the originating side of the connector rather than on the target side. This only happened when the MultiLoc was a peer of the target. The thrown object now correctly lands within the target's top-level location rather than within the origin's top-level room.
    The ComplexContainer class now refers the methods isOpen, isLocked, makeOpen, and makeLocked to the subContainer, if it's non-nil. A complex container appears in the game world as effectively enclosing the contents of its sub-container, because the sub-container doesn't usually appear as a distinct object in the game world, so the apparent open and locked status of the complex container are almost always the same as for its sub-container. These new referrals make it a little easier to write code by removing the need to refer explicitly to the sub-container when checking or changing the open or locked status of a complex container.
    An error in BasicOpenable.makeOpen() caused a run-time error ("wrong number of arguments") in certain cases when a BasicOpenable was used with multiple inheritance. In particular, the error ocurred when an object derived from BasicOpenable also had at least one additional superclass which itself defined makeOpen(), and the additional superclass was placed after the BasicOpenable subclass in the object's superclass list. This has been fixed.
    3.0.7

    Released 6/12/2004

    The default rules in Thing and NonPortable for contentsListed and contentsListedInExamine weren't quite right, so they've been changed slightly. The new default rules are that an object's contents are always listed when the object is directly examined, and they're listed in "indirect" examinations if (1) the object is itself mentioned somehow in the indirect description, and (2) its contents would be listed in a direct examination.

    (A couple of definitions might help. A "direct examination" is what happens when the player explicitly EXAMINEs the object; an "indirect examination" occurs when looking at the room, showing an inventory listing, or directly examining an enclosing object. An object is "mentioned somehow" in an indirect description if either (1) it's included in the basic generated list of portable items within the enclosing object that's being examined, as determined by the isListed property, or (2) it shows as special description, as determined by the useSpecialDesc method.)

    The reasoning behind the new rules is as follows. First, on directly examining an object, its contents should almost always be mentioned; we'd want to conceal the contents only in unusual cases, so the default for contentsListedInExamine is simply 'true'. Second, when indirectly examining an object, we'd normally want its contents to be included in the description, since they're also, indirectly, the contents of the enclosing object being examined; however, when the object itself isn't mentioned in the indirect examination, then we probably don't want to mention its contents, either. Furthermore, if for some reason we've chosen to conceal the contents even when the object is directly examined, then we almost certainly want to conceal the contents in indirect examinations as well.

    The rule is slightly different, and simpler, in NonPortable: contentsListed simply returns the value of contentsListedInExamine. The reason for this different default is that a NonPortable is almost always a fixed feature of its enclosing objects, and as such is almost always mentioned, to the extent it merits mentioning, as part of the custom description of the room or containing object. This is sometimes accomplished with a specialDesc, but not always, so the absence of a specialDesc isn't sufficient reason to think that the object isn't mentioned. Since a NonPortable almost always gets some kind of mention in indirect examinations, then, the default is that its contents are listed in these cases, too, as long as they'd be listed in a direct examination.

    The Consultable class now requires that the object be both touchable and visible for the Consult action (in the past, it only had to be touchable). Consultables are typically things like books or maps whose contents are read visually, so in most cases it only makes sense to consult a consultable when it's visible. This change prevents lookup up information in a consultable while in the dark, for example.
    The new property disambigPromptOrder can be given to any VocabObject (any simulation object with vocabulary words) to control its ordering in an interactive disambiguation prompt ("Which book do you mean, the red book, or the blue book?"). When a disambiguation prompt is shown, the objects in the prompt are listed in ascending order of this property value. By default, disambigPromptOrder is 100; since all objects have this same default, if you don't override it for any objects, the ordering of disambiguation lists will be arbitrary.

    Arbitrary ordering is usually fine. However, it's nice to be able to control the order when objects have ordinals in their names ("first book," "third button," etc), to make the list order match the nominal order. For example, if you have a group of books named "first book," "second book," and "third book," you could give these objects disambigPromptOrder values of 101, 102, and 103, respectively.

    When the parser processes a response to an interactive disambiguation question ("Which book do you mean..."), it now takes a vocabulary match to an ordinal name as superseding the alternative interpretation as a list position. For example:

       Which book do you mean, the red book, the first black book, or the
       second black book?
    

    >second

    The parser will now interpret "second" as referring to "second black book." In the past, it took this to mean the second item in the list, which in this case is "first black book"; this is hardly ever what the player means in these cases, though.

    (This change is actually a bug fix. The parser already had logic to apply this precedence order, but it didn't work properly. In particular, the "adjective ending" preference - the preference for avoiding a phrase that ends in an adjective - superseded the ordinal preference. Adjective endings are obviously not a problem for disambiguation responses, though, so this has been changed.)

    When the parser asks the player for disambiguation help to choose among several items that can be distinguished only by their location, and some of the items are in different top-level locations from the player character, the parser now describes the remote locations using the remote room names (rather than with the default "floor" or "ground" container, as was formerly the case). This helps ensure that objects are distinguished properly when several top-level rooms are connected by sense connectors.

       Which coin do you mean, the one on the floor, or the one on the
       balcony?
    

    In a related change, the parser now chooses a "local" interpretation of a locational qualifier in cases of ambiguity. For example, if the player were to answer the question above with "the one on the floor," the parser would now assume that the player is talking about the coin in the local location (i.e., within the player character's top-level enclosing room). In the past, if the balcony also had a floor, the locational qualifier would have been ambiguous.

    The new function dataTypeXlat(val), defined in the system library file _main.t, returns the "translated" datatype for the given value. This is almost identical to dataType(val), except that when 'val' is an anonymous function, the new dataTypeXlat() returns TypeFuncPtr rather than TypeObject. In most cases, it's convenient to treat an anonymous function pointer value as though it were an ordinary static function pointer, because the two types are almost interchangeable in their usage; this function makes it easier to code such cases, because you don't have to worry about these two different ways that a function-pointer-like value might appear.
    The new class AskTellShowTopic lets you combine responses for an object for ASK ABOUT, TELL ABOUT, and SHOW TO in a single topic response object. Players sometimes find SHOW TO to be more natural than ASK ABOUT when an object is present and visible; this new class makes it convenient when you want to handle either command the same way.
    A bug in Thing.lookAroundWithinContents caused an infinite loop when attempting to describe a location in which three or more top-level rooms were linked by sense connectors. This is now fixed.
    In 3.0.6q, BulkLimiter defined lookInDesc as doing nothing. This was problematic because BulkLimiter is inherited by some special types of containers, such as Underside and RearSurface, that don't have interiors for the purposes of LOOK IN. The problem showed up in Underside and RearSurface objects: attempting to LOOK IN these objects showed no reply at all, hence yielded the library's default "Nothing obvious happens" message.

    BulkLimiter no longer defines this property at all. Instead, this functionality has been moved to Container and Surface. Furthermore, BulkLimiter.examineInterior no longer shows the lookInDesc; instead Container and Surface now show the lookInDesc as part of their action() handler for the LOOK IN command.

    The new Thing method hideFromDefault() is similar to hideFromAll(), but indicates that the object should be hidden from being used as a default for the given action (that is, it won't be considered as a possible default when the player types a command that omits one or more required noun phrases). By default, this simply returns what hideFromAll() returns for the same action, since in general an object that's hidden from ALL should also be ignored for defaulting.

    This is a coarser-grained mechanism than using verify() routines for individual actions, but it's more convenient when you want to exclude an object from being used as a default for a large number of actions.

    gActionIs(a) now returns true if 'a' is one of the specific-direction travel subclasses (NorthAction, SouthAction, etc), and the current action is a base Travel action that's going the same direction. This makes it possible to test for a NORTH command simply by testing gActionIs(North), which is what you would have expected even in past versions if you hadn't known better.

    (In the past, it was necessary to write a test like this instead: (gActionIs(Travel) && gAction.getDirection == northDirection). This two-part test was required because the grammar rules for NORTH, SOUTH, etc. generate the base Travel action instead of the specific-direction subclasses. The grammar rules still do this, but gActionIs() now calls a new Action method, actionOfKind(), which TravelAction overrides to encapsulate the two-part test; since TravelAction does the test for you, there's no longer any need to write it out by hand. You can still write the two-part test if you prefer, and it still works the same as it did before, but it's easier and clearer to just write gActionIs(North) and the like.)

    The algorithm in Actor.tryMakingRoomToHold has been improved slightly. (This is the routine that automatically frees up space in an actor's hands, when the space is needed for the current explicit action, by moving things being held into "bags of holding.") The change is a bit heuristic, but it should work well in most cases. The new rule: when there are at least four items that can be moved into bags of holding, the routine will now find the two that were picked up most recently and move them to the end of the list of possible items. In practice, it's rare to have to put away more than one or two items for a single action, so this change effectively ensures that the items picked up most recently won't be put away automatically.

    The point of this change is to avoid annoying situations where the game automatically puts away an object that you just picked up in order to do something with it. These situations were especially annoying when they were entirely automatic (for example, the game picked up an object automatically, then put it away automatically in order to make room to pick up another object automatically, then had to go get the first object out again in order to use it). It was rare for this to happen, but it looked pretty silly when it did.

    In a related change, the library now arranges the order of putting things away to ensure that if object A goes inside bag of holding B, and B goes inside another bag of holding C, then A will be put in B before B is put in C. This was almost always the result anyway due to affinity ordering, but proper nesting order is now explicitly assured.

    There are two new "illogical" status types that "verify" routines can use to indicate the logicalness of an action with greater precision. The library uses these new types when appropriate in its action handlers. The new types are:
    The Passage class now considers a closed passage to be invisible in a dark room. The reasoning is that, when a passage is open, enough light from the adjoining room (if the adjoining room is lit) comes through the passage to make the passage itself visible, although not enough comes through to light the current room; but when a passage is closed, not even this small amount of light comes through the passage.
    In the past, the parser matched a plural phrase (such as EXAMINE BOXES) to the most likely subset of the matching in-scope objects, as determined by the "verify" logicalness ranking. This was too restrictive; it wasn't at all the common-sense interpretation that most users would expect from a plural usage, which is simply to match everything that matches the vocabulary.

    This has been changed. There are two possible behaviors, which you can control via a global configuration property, gameMain.filterPluralMatches:

    The English parser now handles pronouns better in the recently-added command phrasing TELL actor TO do something. In particular:
    The English parser now rejects the phrasing TELL first actor TO TELL second actor TO do something. (It formerly accepted this, but it didn't handle it as stated: in effect, the first TELL TO was ignored, so the command was treated as simply TELL second actor TO do something.)
    The English parser now accepts the commands SIT DOWN and LIE DOWN. The parser accepts these stated intransitively (i.e., without a direct object), but treats them as synonyms for SIT ON (something unspecified) and LIE ON (something unspecified), respectively. That is, the parser tries to find a suitable default object to sit on, and prompts the player for the missing object it no default can be assumed.
    In the English parser, when the command phrasing involves what looks like an indirect object phrase that doesn't match anything in the grammar, a "miscellaneous preposition phrase" production is matched. This type of production is now more consistent about reporting that "the story doesn't understand that command," to indicate that the structure of the verb phrase was unrecognized. In the past, if the the direct or indirect object phrases were themselves unresolvable, the parser showed that error instead; this was often less useful, because the root problem in these cases is really that the overall verb phrasing can't be parsed.
    The NOTE command now shows a warning if the transcript isn't currently being saved (with the SCRIPT command). This command's main purpose is to let the player embed a comment in the session transcript, as a means of sending feedback to the author. There's not a lot of point in using the command when the transcript isn't being saved, so there's a good chance that a player using the command thinks that a SCRIPT command is already in effect. The warning calls the player's attention to this. The warning is only issued the first time that NOTE is used without logging in effect, but this is reset each time logging is started and then stopped.
    In the past, real-time events created during pre-initialization weren't set up properly, because the real-time manager's internal clock wasn't initialized until run-time start-up. The clock now has a static initializer, ensuring that events created during preinit will have a valid time base.
    3.0.6q

    Released 5/9/2004

    Possible compatibility-breaking change: The standard parser dictionary has been renamed from G_dict to cmdDict (for "command dictionary"). The dictionary is simply an object, and the new name is more consistent with other parser object names (in particular, cmdTokenizer, the standard parser tokenizer).

    It's relatively uncommon for game code to refer to the main dictionary directly, but you should search your code for references to G_dict and change the name to cmdDict.

    Possible compatibility-breaking change: The methods that describe an actor's location have been changed, in order to improve consistency and to simplify the code structure. These changes are mostly internal to the library, so they're unlikely to affect existing code unless you've made some rather low-level customizations.

    The changes involve a lot of renaming and rerouting of internal method calls. The list of changes below is divided into groups of related changes.

    Group 1: The "nominal actor container" methods have been consolidated into a single method.

    Group 2: The Posture object has been taken out of the loop, as it were, for the methods that describe actors. The Posture object's role in the past was to break out these various methods into a separate version for each posture, and then further dispatch to the actor's nominal location: for example, the okayPostureChange method was split up into okayStand, okaySit, and okayLie.

    The original idea was that locations might want to customize extensively by posture, but in practice the multiplicity of methods turned out to be more trouble than it was worth. With the present change, each set of Stand/Sit/Lie variations is now consolidated back into a single method, which makes the Posture object's role unnecessary, allowing it to be removed from the dispatch chain. It's still used in generating the final message, but in most cases this is now a simple matter of getting the posture's participle ("sitting", etc) from the Posture object and using it to build the message string.

    The following list of changes will look daunting, but it's unlikely that existing game code will be much affected, as these methods were always meant mostly for internal use in the library.

    Group 3: Actor "grouping" is now handled in BasicLocation, rather than only in NestedRoom.

    Group 4: Miscellaneous minor changes to the generation of remote room actor descriptions.

    The new TravelConnector method connectorGetConnectorTo() gives a travel connector a chance to scan any "secondary" connectors it knows about for a connection to a given destination. In some cases, a travel connector encapsulates other travel connectors; these are secondary connectors in the sense that they're linked to the room only from the first connector, and thus the room can't find them on its own. This new method provides a way for a room to query a connector for any secondary connectors it knows about, in order to find a connector to a given destination. The base TravelConnector class provides a default implementation suitable for most subclasses, and AskConnector overrides the method to search its list of secondary connectors. Thing.getConnectorTo calls the new method when searching for a connector to a given destination.

    The immediate practical benefit of the new method is that it makes it possible for scripted NPC travel to traverse AskConnectors. In the past, an NPC encountering an AskConnector was typically unable to proceed, since the AskConnector didn't directly link to the destinations of its underlying connectors, and the underlying connectors typically weren't linked directly as directions to the enclosing room. This made it impossible for the scripted NPC to find a suitable connector, and thus blocked the scripted travel.

    The properties for choosing whether or not to list the contents of an object to list in EXAMINE and LOOK AROUND descriptions have been expanded slightly to provide more control and better consistency. These changes are all in the Thing class.

    The isListed property now returns, by default, the value of isListedInContents. The meanings of these properties haven't changed: isListedInContents controls whether or not the object is listed when examining the object's direct container, whereas isListedInContents controls listability for examining indirect containers, including the enclosing room. The reason for this change is that it's rare that we'd want an object to be unlisted when its direct container is examined, but still want it to be listed when enclosing containers are examined (whereas the reverse isn't entirely uncommon, since we sometimes want to mention an object only as a detail of its direct parent, but not of the broader room context).

    The new property contentsListedInExamine lets you control whether or not an object's direct contents are listed when the object is examined directly. In the past, you could do this only by overriding examineListContents, but the new property makes it easier to control this behavior. The new property returns the same thing that contentsListed used to return.

    The contentsListed property now returns the value of contentsListedInExamine by default. This is parallel to the change to isListed.

    The new method getListedContentsInExamine() returns the list of direct contents that should be listed when the object is examined. This new method is the direct-examination equivalent of the existing getListedContents() method, and returns the same value that getListedContents() formerly returned: the subset of direct contents that appear in the sense info table passed to the method.

    The getListedContents() method now simply returns the result of calling getListedContentsInExamine().

    The method examineListContentsWith() now calls getListedContentsInExamine() to obtain the list of direct contents to list when examining an object. In the past, this used the 'contents' property. The method also checks the value of contentsListedInExamine, and skips the entire listing if this property is nil.

    These changes are all designed to be compatible with existing code. They should simply add more flexibility and make certain behaviors easier to customize, but existing code shouldn't be affected.

    In 3.0.6p, the library started listing the visible, portable contents of "remote" rooms as part of each room description. The implementation had a problem when MultiLoc objects were involved, though: if a MultiLoc object appeared in more than one of the connected top-level rooms and had portable contents, those contents were listed several times - once for each connected top-level room in which the MultiLoc appeared.

    This problem has been corrected: the room description generator now ensures that a MultiLoc's contents appear only once. To do this, the room description code now excludes each object that is also located in the "local" top-level room or in any connected remote room whose contents listing has already been generated.

    NestedRoom now overrides roomRemoteActorDesc() to describe the actor as being in the nested room. In the past, if the nested room's location was visible, the nested room deferred to the location to provide the description. This produced misleading messages, because it described the actor as being in the outermost visible room containing the actor - which was technically true, but left out the important information about the intermediate nested room.
    Noise and Odor objects no longer include themselves in LISTEN TO ALL and SMELL ALL, respectively. These objects formerly did include themselves in ALL for these commands, since the commands are obviously highly applicable. However, because these objects are intangible, it usually doesn't make sense from the player's perspective to consider them in ALL, which a player usually thinks of as applying to the set of discrete, visible objects. In practice, including these in ALL was often problematic because these intangible objects are usually not given names or vocabulary; they usually exist only to model a feature of their source object, and aren't meant to look like separate objects to the player.

    Along the same lines, Vaporous objects are now included in EXAMINE ALL, SMELL ALL, and LISTEN TO ALL. These objects do have a visible presence, so it makes sense to include them in commands that examine everything in sight with any sense.

    For particular Noise and Odor objects that you do want to participate in LISTEN TO ALL and SMELL ALL, just override hideFromAll() to return true for the desired action or actions.

    NonPortable now overrides the inherited Thing verify() handling for EXAMINE to make an un-held NonPortable as likely as a held Thing for disambiguation purposes.

    Recall that, by default, Thing downgrades the disambiguation likelihood that EXAMINE refers to an object not being held. The reasoning is that an object being held is physically closer to the actor, so in a real-world situation, it would be more convenient to closely inspect an object being held than one sitting nearby. Since NonPortable objects can't be held, though, this reasoning breaks down somewhat. The change removes the disambiguation disadvantage for NonPortable objects.

    The objHeld precondition uses a new Thing method, meetsObjHeld(actor), to determine if the object meets the condition. This new method is called instead of isHeldBy(actor) on the given object. This change allows an object to indicate that it's not actually being held by an actor, but that it's as good as held for the purposes of the objHeld condition. It's rare for meetsObjHeld() to return anything other than isHeldBy(), but does happen occasionally. The most obvious case is body parts: these can't be considered to be held in the actor's hands (unless the actor is specifically doing so for some reason), but at the same time, there's no reason the actor should have to hold a body part in order to carry out actions with objHeld conditions.
    The Wearable class now handles SHOW TO specially: if the object is being worn by the actor doing the showing, it doesn't require that it be held. In the past, the objHeld condition was applied, as it is by default to any portable object, which meant that the character had to take off a wearable in order to show it to someone; this doesn't usually make a lot of sense, thus the change.
    In Thing, remoteSpecialDesc(pov) now calls distantSpecialDesc by default; distantSpecialDesc in turn calls specialDesc by default. In the past, remoteSpecialDesc called specialDesc directly by default. This change leaves the default behavior the same as before, but it simplifies the common case where the "distant" and "remote" descriptions are the same by letting a single override - distantSpecialDesc - handle both descriptions.

    Similarly, remoteInitSpecialDesc(pov) now calls distantInitSpecialDesc by default (rather than initSpecialDesc, as it did in the past).

    Room now treats the GetOutOf action as equivalent to Out. (This means that if the room has vocabulary, the player can now say "get out of (room)" as a synonym for "out".)
    The English verb grammar now accepts LEAVE as a synonym for EXIT.
    The English parser now accepts "A" and "T" as abbreviations for ASK and TELL, respectively, in the full phrasing with ABOUT. That is, you can now say, for example, A BOB ABOUT BOOK. In the past, the A and T abbreviations could only be used in the super-short forms of the commands, where only the topic could be specified (as in T BOOK).
    The English parser now accepts the formats ASK actor TO command and TELL actor TO command as equivalent to the traditional actor, command format for telling an actor to do something. The new ASK TO and TELL TO formats are simply syntactic variations; they're otherwise handled exactly the same way as the traditional actor, command format.
    In the English library, SpecialTopic now allows a variation on the input format to accommodate players who are thinking in terms of ASK/TELL. Now, when a SpecialTopic is active, and the player's input doesn't match the special topic's input pattern, and the user's input starts with "A" or "T" as a separate word, the SpecialTopic checks for a match to the rest of the player's input (i.e., the part following the "A" or "T"). For example, if the special topic is "apologize for forgetting the anniversary," and the player types T ANNIVERSARY, the special topic will try dropping the "T" and matching just the "ANNIVERSARY" part.

    This change is designed to accommodate players who are accustomed to the standard ASK/TELL format, and who might not realize that the special topic list shows commands meant to be entered literally, or who are just so in the habit of using ASK/TELL commands that it doesn't occur to them to leave out the "A" or "T" part. Since an "A" or "T" command pretty clearly indicates that the player's intent is to communicate with the NPC, it makes sense to check the topic the player is trying against any active special topics.

    Note that the special topic parser only accepts the super-abbreviated formats - the "A" and "T" commands. This is because it seems much less likely that it would even occur to a player to try a special command with the full ASK ABOUT or TELL ABOUT phrasing. Once the command is expanded to the full phrasing, the detailed syntax of the special command suggestions should make it fairly obvious to the player that the ASK ABOUT or TELL ABOUT part isn't needed. In contrast, since the abbreviated "A" and "T" formats are ungrammatical to start with, it would seem perfectly natural to use them with arbitrary special command suggestions.

    The RoomPart class now downgrades its logicalness for Examine commands, in the same way that Decoration does. Room parts are generally so much background noise, included only in case a player explicitly refers to them. Downgrading the logicalness helps prevent the room parts from causing nuisance disambiguation questions.
    Due to a parser bug, the phrase ALL IN container (which can also be written ALL FROM, ALL THAT'S IN, etc) incorrectly ignored the hideFromAll() status of the objects in the container when determining which objects to match to the phrase. This has been corrected; the phrase now respects the hideFromAll() settings of the objects involved.
    The English parser now matches words it has parsed as "literal adjectives" to ordinary adjectives in the dictionary when resolving a phrase in "global scope." This change is the literal-adjective equivalent of the similar changes involving noun phrases in global scope introduced in 3.0.6p.
    Noun phrases that consist entirely of a "literal adjective" word are now marked as "adjective-ending" phrases for the purposes of ranking grammatical pattern matches during parsing. (The omission of this flagging in the past was an oversight.)
    Due to a bug, the removeEvent() method of the RealTimeEvent class (the common base class for real-time fuses and daemons) didn't work properly. This has been corrected.
    A bug in the Lister class caused in-line contents listings to use the "long list" format (i.e., with semicolons as the separator between items) if the enclosing list did. An in-line contents list is set off by parentheses, so it stands as an independent list and thus should a long-list format only if it would need to on its own, irrespective of the needs of the enclosing main list. (At least, the parentheses are used in the English library; other languages might use different notation, but in any case should use a notation that makes the contents list similarly independent of the enclosing list.)
    A couple of minor changes to the default LOOK IN handling make it a little easier to customize the behavior, and reduce the need to do so.

    First, Thing has a new property, lookInDesc, that gives a message that's displayed by default in response to LOOK IN and SEARCH. This shows the standard default ("there's nothing unusual in it") if you don't provide a custom message. It happens frequently that a game wants to depict an object as having some interior features, but doesn't want to actually model those interior features as separate objects. The new lookInDesc makes this more convenient, since you only need to provide the custom message in this property, rather than overriding the entire dobjFor(LookIn) handling, as was necessary in the past.

    Second, Decoration now treats LOOK IN as logical by default (in the past, this was handled by the catch-all routine that makes most actions respond that the object is "not important"), and applies the same logical-rank downgrade applied to EXAMINE. Correspondingly, the default lookInDesc for Decoration displays the object's "not important" message. This means that Decoration objects act by default as they did before, displaying "that isn't important" in response to LOOK IN, but can be given a custom LOOK IN response simply by setting lookInDesc, just as with any Thing.

    Third, the English phrasing for finding nothing in an object specifically searched with LOOK IN or SEARCH has changed slightly, to report that there's nothing "unusual" in the object. In many cases, an object is described as having what would in the real world be interior features, but those interior features aren't modeled as actual game objects and hence don't show up by default in response to LOOK IN or SEARCH. The slight change in phrasing helps make the response at least technically defensible. It's probably still better in most cases to customize the LOOK IN response anyway, but at least the default response is a little less jarring in these cases.

    Decoration now treats READ as logical by default, the same as it does EXAMINE. (As mentioned above, LOOK IN now has the same treatment as well.)
    In the Thing class, PUT X IN X and PUT X ON X now respond with custom messages ("you can't put the X in/on itself"). In the past, the catch-all message indicating that X isn't a container/surface was used, which is a less conspicuous problem than attempting to put something in or on itself.
    In the English library, the plural-to-noun match expansion introduced in version 3.0.6p is now limited to "global scope," just as the noun-ending-to-adjective-ending match expansion already was. English plurals are usually formed by adding "s" or "es" to the end of a word, so when a noun is six letters or longer, it usually looks like a truncated form of its plural; this interacted with the plural-to-noun match expansion to be overly aggressive in upgrading phrases to a plural interpretation. The match expansion is only really important in global scope anyway (for the reasons why, see the explanation of the noun-to-adjective expansion), so this change maintains the primary benefits of the match expansion while avoiding the weird cases that can come up in local scope.
    The parser now considers an indefinite noun phrase to be less desirable, for grammar selection purposes, than a definite interpretation of the same phrase. Object names can occasionally look like indefinite phrases: an elevator button labeled "1" could be called a "one button," for example, and a subway might have an "A train." When there's an interpretation that exactly matches the phrase to the name of an in-scope object, the parser will now prefer that interpretation over one that treats a word like "a" or "one" as signifying that an arbitrary match to the other words should be chosen.
    A library bug that caused a run-time error in Actor.findVisualObstructor() has been corrected.
    The library now executes "prompt daemons" (the special type of daemon that runs before each command prompt, rather than when the turn counter changes) before every prompt, rather than just before a main command prompt. This ensures that various miscellaneous tasks are performed whenever the game pauses for user input, even when input is requested in the course of processing a command.
    3.0.6p

    Released 4/25/2004

    Compatibility-breaking change: The property initDesc, and the related properties, have been renamed. Games that define or use these properties will have to change to use the new names. You should search all of your existing source for the old names, and replace each one with the corresponding new name.

    Important: you must search for "initDesc" and replace it with "initSpecialDesc" first. This is required because the former "initExamineDesc" has been renamed to "initDesc" - so you have to make sure you change all the old "initDesc" instances before you rename "initSpecialDesc" to create new "initDesc" instances. Please do your search-and-replace operations in the order shown below:

    The purpose of this change is to make the naming more consistent. The old usage - "initDesc" for a special description and "initExamineDesc" for the EXAMINE description - was inconsistent with the respective non-initial properties, "specialDesc" and "desc". The qualification was reversed: "specialDesc" was qualified as special, leaving the EXAMINE description unqualified as simply "desc", while the initial descriptions qualified the EXAMINE desription rather than the special one.

    Minor compatibility-breaking change: The Attachable class's scheme for explaining why the object can't be attached to a given other object has been changed. In the past, the method cannotAttachMsg(obj) returned a message string or actor-action-messages property, but this created a conflict with another message property of the same name, which is used by Thing. To resolve the conflict, the Attachable method has now been renamed to explainCannotAttachTo(obj), and rather than returning a message to display, the new method simply displays the message itself.

    If you have any Attachable objects that define cannotAttachMsg, you must make two changes to each. First, rename cannotAttachMsg to explainCannotAttachTo. Second, rather than returning a string, display the string directly.

    Possible compatibility-breaking change: The conversation mechanism has some internal changes to support deferral across hierarchy levels (see below).

    Note that the TopicDatabase methods findTopicResponse() and handleTopic() no longer need the topic list parameter because this information can be obtained from the new convType parameter.

    These methods are mostly for internal use by the library, so the likelihood that your existing game code will be affected is small. Even so, you should scan through your code for these methods and make the appropriate adjustments.

    Important: note that there are two related but independent methods called handleTopic(). There's the TopicDatabase method, which is affected by this change, and there's the separate TopicEntry method, which is not affected. When you're scanning your code, you only need to change the TopicDatabase version. It's easy to tell which is which at a glance: the TopicDatabase version takes three parameters, while the TopicEntry version only takes two. When you're scanning your code, only change the three-parameter instances.

    Additional technical notes: you'll only need to read this part if you're making some rather advanced overrides to the library. Please skip this part unless you find something that you're not sure how to handle in the search-and-replace step above.

    If your code contains calls to any of the affected methods, and you're wondering what to pass for the convType parameter, you should first check to see if you're receiving that value as a parameter to the calling code; if so, just pass that parameter down. This is usually easy to spot at a glance. If you have code like this:

       handleTopic(self.(convType.topicListProp), actor, topic);
    

    then you can simply change it to this:

       handleTopic(actor, topic, convType, nil);
    

    The important thing to note here is that you're already using the convType parameter to look up the topic list in the old-style call; you can simply drop the topic list lookup and pass the convType parameter itself instead (but note that it moves to the last position).

    If you don't have a convType parameter passed in from your caller, you'll need to choose which one to use. Look at which topic list you're passing, and think about what conversational action is triggering your code. Then scan through the list of xxxConvType objects (askAboutConvType, tellAboutConvType, yesConvType, noConvType, etc) defined in actor.t, and choose one that (1) has the same topic list property that you're already using, defined in its topicListProp property, and (2) conceptually matches the action you're performing. If you can't find anything appropriate, you might simply need to define your own ConvType object for whatever extended action you're defining; just create one following the same pattern as the existing ones in actor.t.

    You can usually just pass nil for the new fourth parameter ('path') to handleTopic(), findTopicResponse(), and ConvNode.handleConversation(). You only have to pass a non-nil value when you're implementing a search through a hierarchy of topic databases, in which case you'll have to pass a list of the inferior databases in the hierarchy. The purpose of the new parameter is to allow a higher-level database to defer to a match in a lower-level database, if one exists, so at each level you must pass the remainder of the "search path" of databases.

    The above changes to the conversation methods are designed to facilitate a new mechanism that allows a conversation TopicEntry in one topic database to defer to a TopicEntry from an inferior database. This is done through the new TopicEntry method deferToEntry(entry): this method returns true if 'self' should defer to 'entry', nil if not. 'entry' is always a non-nil TopicEntry object from an inferior topic database.

    Recall that topic entry databases for conversation are arranged into a three-level hierarchy for each actor: at the top is the ConvNode's list of topics, at the middle level is the ActorState list, and at the bottom level is the Actor list. We find a matching topic entry by scanning each level of this hierarchy in turn, and taking the first match we find. So, if there's a ConvNode match for a topic we're looking for, we use it, even if there's a match for the same topic in the ActorState or Actor. The matchScore is irrelevant: the three-level hierarchy always trumps the matchScore ranking. The matchScore ranking is only used to choose among different matching entries at the same hierarchy level.

    The new deferToEntry() mechanism allows topic entries to defer handling across hierarchy levels. Here's how the new scheme works: before we start searching the hierarchy, we start by noting the matching entry at each level. Then, we search each level from the top down as before. At each level, though, we look at the winning match, and ask it via deferToEntry() if it wants to defer to the winning match from the next level down the hierarchy. If so, then we ignore the match at that level. This means that we skip the higher-level match and go straight to the lower-level handling.

    The main purpose of this addition is to allow a DefaultTopic entry to be a catch-all only for topics that aren't handled explicitly at a lower hierarchy level. For example, to create a DefaultTopic that defers to any non-default match at a lower hierarchy level, add this to the DefaultTopic object:

       deferToEntry(other) { return !other.ofKind(DefaultTopic); }
    

    This tells the topic finder that if there's any match that's not itself a DefaultTopic at a lower hierarchy level, then this default should be ignored.

    The English library template for Room has been extended, and the default relationships that derive one type of Room naming property from another have changed slightly. These changes should be fully compatible with existing game code, and should make it easier to customize the various kinds of room names.

    The template for Room now takes the first string to be the 'roomName' property, the second to be 'destName', and the third to be the 'name' property. The 'desc' property is still in the last position.

      Room template 'roomName' 'destName'? 'name'? "desc"?;
    

    In the past, the 'name' was the first entry, and 'destName' was the second. The library took the 'roomName' to be the same as the 'name'. However, it makes more sense to do this the other way around: the 'roomName' is now the one that's explicitly defined by the game, and the 'name' setting is optional. If the 'name' setting is omitted, it's derived by default by converting the 'roomName' to lower case.

    The 'roomName' is what's shown as the title of the room, in the room description and on the status line. This is usually given in "title case," meaning that each word has an initial capital letter, with the exception of minor function words: "Hall of the Ancient Kings". This is the format that games have defined all along, but in the past, they defined it using the 'name' property. The change improves matters, because it allows the room's ordinary 'name' property to be entered separately, in a format suitable for use when the name is needed in library messages: "You can't have the ice cave."

    The default 'name' derivation - converting the 'roomName' string to lower case - gives decent results a lot of the time. However, in many cases, you'll want to customize the ordinary name separately. The template makes this easy by adding a slot for the 'name' property separately from the 'roomName'.

    There's one additional change to the default derivations. In the past, the 'destName' didn't have a default at all. Now, the default is the room's 'theName', which is itself derived, by default, by prepending the ordinary name with 'the'. As before, the template lets you supply a separate, custom 'destName' value.

    The MultiLoc object now transmits sense information to and from its contents in a more consistent fashion. In the past, putting things inside a MultiLoc had somewhat inconsistent results; the behavior should be more predictable now.

    The primary design principle of a MultiLoc hasn't changed: a MultiLoc is a single object that appears in multiple locations, but which doesn't provide any sense connection among the multiple containers. A MultiLoc is a single object in the sense that the same object appears, in its physical entirety, in each of its containers.

    In the past, a MultiLoc was essentially a "sense ceiling": sense information propagated from outside the MultiLoc to its interior, but didn't propagate from within the MultiLoc to its containers. This was intended to prevent the MultiLoc from propagating sense information among its containers, but it was too restrictive an implementation. For example, if a MultiLoc contained a flashlight, the flashlight's light didn't shine into the MultiLoc's locations. Similarly, if an NPC was inside a MultiLoc, the NPC couldn't see or hear anything from any of the MultiLoc's locations, and hence couldn't take part in a conversation with the PC while the PC was outside the MultiLoc.

    The change is that a MultiLoc is no longer a sense ceiling; it's now more like a wall. Now, from a point of view inside the MultiLoc, all of the MultiLoc's locations (and their contents, to the extent that's appropriate) are visible. From the point of view of any of the MultiLoc's containers, the MultiLoc and its contents (as appropriate) are visible, but none of the MultiLoc's other containers are visible. Light is similarly transmitted from inside the MultiLoc to all of its containers, but not from one container to another. So, for example, if a flashlight is inside a MultiLoc, it provides light to every container of the MultiLoc; but if a flashlight is in one of a MultiLoc's containers, the light doesn't make it through the MultiLoc to its other containers.

    The main effect of this change is that things located inside a MultiLoc now behave the way one would expect them to. In the past, the asymmetrical "sense ceiling" design led to some odd behavior when putting objects inside a MultiLoc: an actor within a MultiLoc couldn't carry on a conversation with someone outside, for example, because the actor inside couldn't hear out beyond the MultiLoc. With the change, MultiLoc should behave much more intuitively.

    The ContainerDoor class now defines the isOpen method to return the same thing as its associated container's isOpen method; and it now defines examineStatus to display its associated container's open/closed status.
    A ComplexContainer can now be used as a bag of holding. Operations involving the bag-of-holding features of a complex container are simply passed redirected to the complex container's subContainer object.
    Certain object contents listings were grammatically incorrect when showing a single item that had a plural name. For example, if an item had the name "some coins," and it was the only item on a table, the table description said "On the table is some coins."

    The listers now take into account the "cardinality" of each item listed, to ensure grammatical agreement in these cases. For English, there are only two degrees of cardinality that matter: one, and many. The English module therefore assigns a grammatical cardinality of 2 to items whose isPlural property is set to true, 1 for all others; this is done with the new Thing method listCardinality(lister). A lister calls this new method to determine the cardinality of the list, so that it can provide the correct information to the methods that generate the messages.

    The LISTEN TO command now lists not only the sound the object being examined is making, but the audible sounds of its contents as well. This ensures that sounds made by components of an object are properly displayed when the enclosing object is examined, and also ensures that a container that has a sound source inside will have its sound listed.

    The SMELL command has been similarly enhanced.

    As part of this change, the thingListenDesc and thingSmellDesc messages have been moved from libMessages to playerActionMessages, and renamed thingListenDescMsg and thingSmellDescMsg, respectively. These messages are now shown as default description messages, necessitating the move to playerActionMessages.

    The Distant class now explicitly allows LISTEN TO, the same way it allows EXAMINE.
    It's now possible to create a SenseConnector that occludes some objects from view, depending on the point of view. This is useful for situations where a sense connector provides only a partial view of another room. For example, consider two rooms with a window between them, providing a view of one room from the other. Now, suppose a bookcase is positioned with its back to the window. When viewing the room with the bookcase from the other side of the window, it's not possible to see the contents of the bookcase.

    The new class, OccludingConnector, is a mix-in that you can combine with SenseConnector to create a partially occluded sense path. Put OccludingConnector before SenseConnector in the class list. You specify which objects to occlude from view by defining the method occludeObj(obj, sense, pov). 'obj' is an object to test for occlusion; 'sense' is the sense being calculated; and 'pov' is the point of view, which is usually the actor performing the command. Your method must return true if the connector occludes the object, nil if not.

    To implement the window in our example above, we'd do something like this:

      OccludingConnector, SenseConnector, Fixture 'window' 'window'
        "It's a small window. "
        connectorMaterial = glass
        locationList = [roomA, roomB]
        occludeObj(obj, sense, pov)
        {
          /* from roomA, we can't see what's in the bookcase in roomB */
          return (pov.isIn(roomA) && obj.isIn(bookcase));
        }
      ;
    

    OccludingConnector uses a new mechanism that can be used for more extensive customization of the sense path calculation than OccludingConnector itself enables. During each sense path calculation, the library first builds a table of all of the objects connected by containment to the point of view. (This table contains all of the objects that can possibly be sensed, because sense paths are based entirely on containment relationships.) The library then calls clearSenseInfo() on each item to initialize it for the path calculation.

    In clearSenseInfo(), an object can register itself for an additional notification at the end of the sense calculation. To register for the notification, override clearSenseInfo(), inherit the default code, and append 'self' to the notification vector:

      clearSenseInfo()
      {
        inherited();
        senseTmp.notifyList.append(self);
      }
    

    Now that the object is registered, it will be notified at the end of the calculation, after the sense path to each object is fully calculated, but before the final table of sense paths is constructed. The notification method is called finishSensePath(objs, sense). 'objs' is a LookupTable whose keys are the objects connected by containment to the point of view; 'sense' is the Sense object of the calculation.

    You can do anything you want to the sense table in finishSensePath(). The sense path information for each object is stored in the object's tmpXxx_ properties - tmpTrans_, tmpAmbient_, tmpObstructor_, etc. To change an object's sense path, simply change those properties. (OccludingConnector simply sets each occluded object's path transparency, in tmpTrans_ and tmpTransWithin_, to nil to indicate that the object cannot be seen.) You can make additional objects visible by adding them to the lookup table and setting their tmpXxx_ properties to the settings you want them to have.

    Note that the notification step is required for performance reasons. The library could call the notification method on every object, which would simplify things by eliminating the need for the extra line of code to register. However, very few objects are likely to require this extra notification, and since sense path calculations are performed very frequently, the library doesn't want to call the notification method blindly on all objects. So, to avoid adding the notification overhead to every object, the library requires the rare objects that require the notification to register themselves.

    The logic that decides on the descriptions generated for distant and obscured objects has changed slightly. The purpose of these changes is to make it easier to customize the various ways an object is described, and to make the selection of description more intuitive.

    First, a new type of description has been added: the "remote" description. There are three new remote description methods: remoteDesc, for the EXAMINE description; remoteSpecialDesc, for the special description; and remoteInitSpecialDesc, for the remote initial special description. These new methods are used to provide the examination and special descriptions when the object is viewed from a separate top-level location. For example, if two top-level rooms are connected by a window, so that an actor in one room can see objects in the other room through the window, the remote description is used to describe objects in the other room from the actor's perspective.

    Second, when choosing a special description (as part of a room or contents listing), the logic is now as follows:

    In the past, the sightSize was taken into account in selecting the type of special description to show: the specialDesc was shown if the object had a large visual size, regardless of the transparency of the sense path. Now, the sightSize isn't considered at all. The reason for the change is that the distantDesc and obscuredDesc are almost always custom messages per object, so they will naturally take into account the visual size of the object; considering the visual size added an unnecessary extra dimension.

    Note how the new remoteSpecialDesc fits into the selection rules: the remote special description takes precedence over the obscured and distant descriptions. That is, if the object being described is in a separate top-level room from the actor doing the looking, the remoteSpecialDesc is used, even if the object is also visually obscured or at a distance.

    Third, when choosing an examine description (when the object is explicitly examined), the logic is now as follows:

    The logic for deciding whether or not to use the initial special description and initial examine description for an object has been refactored slightly. The refactoring yields the same default effects as before, so existing game code won't be affected, but the changes provide better control for overriding the default behavior.

    First, the new Thing method isInInitState determines whether or not to use the initial special and examine descriptions. By default, this method returns true if the object has never been moved, nil if the object has been moved. That is, the object is considered to be in its initial state, for the purposes of descriptions, until the first time the object is moved by an actor in the game.

    Second, the new Thing method useInitExamineDesc() indicates whether or not the initExamineDesc should be used when examining the object. By default, this returns true if the object is in its initial state, as indicated by isInInitState, and the object defines a non-nil initExamineDesc. Since isInInitState by default returns true if the object has never been moved, and nil otherwise, the default behavior is the same as before, so existing games will not be affected by this change.

    Third, the existing Thing method useInitDesc() looks at isInInitState to make its determination. Since isInInitState by default returns true if the object has never been moved, this has the same effect as in the past.

    These changes make it easier to change the "never moved" logic that determines whether or not to use initial descriptions, and also allows you to control the use of the initial special and examine descriptions independently of one another. For example, if you want the initial examine description to be used one time only, rather than to be based on whether the object has been moved, you could override useInitExamineDesc() to base its determination on 'described' rather than on 'moved'.

    In the English library, Room has a new method that provides a prepositional phrase that can be used to describe objects as being in the room. The new method is inRoomName(pov), and it returns a string that can be used to construct a sentence describing objects as being in the room. 'pov' is the point of view; this might not be in the same top-level room, as we could be viewing objects in this room from the point of view of a separate room that's connected to our room by a window or another sense connector.
    Room descriptions now include listings of the portable objects visible in separate top-level locations. In the past, only the contents of the point-of-view actor's own enclosing room were described; now, the contents of each connected top-level room are described as well.

    To generate the new remote room contents listings, the room describer first determines the set of top-level rooms with items that are visible from the point of view of the actor doing the looking. The room describer shows the contents of the local room (the room containing the point of view) first, using the same procedure it always has. Next, the describer runs through the set of other top-level rooms with visible contents; for each one, it generates a separate remote contents listing.

    Each remote room's contents listing is displayed using essentially the same procedure used to generate the local room's contents listing. However, instead of using the lister defined by the local room's roomContentsLister property (which is set to the roomLister object by default), each remote listing is generated using a lister returned by the new method remoteRoomContentsLister(other). This method is called on the point-of-view room, and 'other' is the remote room whose contents are to be listed. By default, this returns a lister that uses the remote room's inRoomName to generate the description.

    You can customize the listing of portable items visible in remote locations by returning a custom lister from remoteRoomContentsLister(). Note that the new class CustomRoomLister makes this easy, if all you want to do is to customize the prefix and suffix messages displayed in the listing:

      remoteRoomContentsLister(other)
      {
        return new CustomRoomLister('Through the window, {you/he} see{s}', '.');
      }
    

    Note that remoteRoomContentsLister(other) is called on the local room - the room containing the actor doing the looking - and 'other' is the remote room. This arrangement lets you customize the listing in the point-of-view room, which makes sense in that the overall description is of the point-of-view room.

    When a room is described (as when entering the room, or examining it with a LOOK AROUND command), everything visible from the room is marked as having been seen. In the past, only visible objects that were located within the actor's top-level enclosing location were marked as visible; now, everything that's visible is marked as seen, including objects in other top-level rooms that are visible from the actor's location.
    The library has an enhanced way of listing an actor's presence in a room (as part of the room description) when the actor is visible in a remote location (i.e., a separate top-level room) or at a distance. In the past, there was no distinction between the normal case and the distant and remote cases. Now, when the actor is in a remote room, or at a distance, a different path is used to generate the message.

    To accomplish this change, Actor now overrides remoteSpecialDesc and distantSpecialDesc. On LOOK AROUND, the room calls the appropriate one of these to describe the actor when the actor is at a distance. (The normal selection procedure is used: the remoteSpecialDesc is used if the NPC is in a separate top-level location from the point-of-view actor, otherwise distantSpecialDesc is used if the NPC is at a distance.) The new Actor implementations of remoteSpecialDesc and distantSpecialDesc invoke the corresponding new ActorState methods of the same names. These new ActorState methods in turn call back to the new Actor method actorThereDesc (both the distant and remote methods call this one new method). The new actorThereDesc calls the new method roomRemoteActorDesc on the actor's location. Thing provides a default implementation of this new methods, which simply calls the same thing on its the location if there's a location that's visible from the point of view, or shows a default library message if not. The default library message method is also called roomRemoteActorDesc. This uses the location's inRoomName to generate the description.

    This sequence of calls lets you hook in and customize the actor's distant special description at whatever level is most convenient - at the actor itself, at the ActorState, or at the containing room. In most cases, it will probably be most convenient to customize this in the room, to display a message customized to the appearance of the room from a distance. To do this, you'd put a method like this on the room:

      roomRemoteActorDesc(actor)
      {
        "\^<<actor.nameIs>> over at the north end of the courtyard. ";
      }
    

    Note that the actor to describe is provided as a parameter, and that the method is responsible for displaying the complete message describing the actor's presence.

    In the past, an EXAMINE command applied to a room was always treated as a LOOK AROUND command; this didn't produce good results when the room being examined was a separate room that the actor doing the looking wasn't in (i.e., a room connected to the actor's location via a sense connector of some kind). This no longer happens; when the actor doing the looking isn't inside the room, the standard EXAMINE handling is used, as though the room were any ordinary object.
    Thing.lookAroundPov() now actually sets a global point-of-view object. This ensures that the POV information can be obtained (via the getPOV() function) when room descriptions are being shown.
    Thing.addToSenseInfoTable() no longer includes the 'src' parameter. Instead, the same information can be obtained from senseTmp.pointOfView. (The parameter has been removed for effiency: the information is rarely needed, and is available from this other source when it is needed.)
    The Thing method canDetailsBeSensed() now takes an addition parameter giving the point of view. This is usually the actor performing a LOOK AROUND, EXAMINE, or other command that's causing a description of the object to be generated.
    Thing.lookAroundWithinSense now marks each item with a "presence" in the sense as known to the actor who's doing the looking. This ensures that any noises and odors that are actively listed become known to the actor as soon as they're listed. Objects are marked as known rather than seen, because it might be possible to hear or smell objects that can't be seen. Note that only objects with a "presence" in the sense are marked as known; an object with a presence in a sense is one that is actively calling attention to itself in the sense, via a noise or odor message in the room description. Thus, an object that isn't actually making any noise worth listing doesn't become known just because an actor could hear it if it were making noise; it has to actually be making some noise (and say so by setting its soundPresence to true) to become known in this manner. Note also that a noise and its source are frequently separate objects (likewise with odors), and it's the noise that becomes known in this arrangement, not the source. This is important because it's often possible to hear the sound a thing makes without knowing what's making the noise: we can hear a beeping coming from inside a closed box, so we now know about the beeping, but we can't tell that it's coming from an alarm clock, so the alarm clock remains unknown to us.
    By default, Thing now applies the objHeld precondition to a Show action; this means that SHOW X TO Y requires X to be held if X is an ordinary Thing. In addition, NonPortable only requires that the object be visible. This change assumes that portable objects need to be held up for someone else to look at, as though offering the object to the other person, while non-portable objects can be merely pointed out but left in place.
    The Vehicle class now treats an OUT command while an actor is inside the vehicle as meaning to drive or ride the vehicle out of the vehicle's enclosing location. In the past, OUT in a vehicle meant the same thing as in a nested room: GET OUT of the vehicle. The old behavior didn't seem to match most players' expectations, and it had the drawback that it left no standard way of expressing the intention of driving/riding the vehicle out of its location.

    For certain vehicles, it might still be desirable for OUT to mean GET OUT. Fortunately, it's easy to override the default behavior on a case-by-case basis - just override a vehicle's 'out' property to return nestedRoomOut:

       car: Vehicle
         out = nestedRoomOut
    
         // ...other properties...
       ;
    
    In the past, RoomConnector and OneWayRoomConnector didn't apply the proper preconditions to travel; in particular, they didn't properly determine the required initial location for the travel, so they didn't move an actor out of a nested room before attempting the travel. This has been corrected; RoomConnector now uses the travel preconditions formerly defined in RoomAutoConnector, so all RoomConnector subclasses (including OneWayRoomConnector and RoomAutoConnector) now use the correct preconditions.
    Enterable and Exitable objects now add a "touchable" pre-condition to travel through the objects. In the past, only the pre-conditions for the underlying connector were applied; since some types of travel connectors are not themselves physical objects, this occasionally led to odd results. In particular, if an Enterable or Exitable was visible but not touchable (for example, if the object was at a distance, or separated from the actor by a window), it was possible to enter it despite its being unreachable. This type of anomaly should no longer occur.
    The departure and arrival messages for NPC's now yield more pleasing results in cases where an NPC arrives or departs from a visible "remote" location - that is, a location not part of the PC's outermost room, but connected to the PC's location by a sense connector (such as a window between two rooms, or a DistanceConnector). There are two cases where the departure and arrival messages are enhanced.

    First, when an NPC departs from or arrives at a remote location, and the other end of the NPC's travel is out of sight, the library adds the name of the remote location to the arrival or departure message. For example: "Bob leaves to the east from the alley," or "Bob comes down the stairs to the north end of the courtyard." The name of the location is taken from the destName property of the NPC's location. The location name is only shown when the location is remote; when an NPC enters or leaves the PC's outermost room, the traditional messages ("Bob leaves to the east," etc.) are shown. The addition of the location name to the message helps clarify that the NPC is moving around nearby and within sight, but isn't directly at the PC's location.

    Second, when an NPC starts and finishes its journey within sight of the PC, the library omits the departure message entirely, and displays a new type of arrival message. When the entire journey is within the PC's field of view, we don't wish to describe the departure and arrival as two separate steps, since the whole journey can be described as a single operation; this is what the new type of arrival message does. The new message is given by the new TravelConnector method describeLocalArrival(), which the traveler calls from its describeNpcArrival method when it determines that the origin is visible to the PC. TravelConnector.describeLocalArrival() in turn calls the new Traveler method sayArrivingLocally(), and the default implementation of this new method simply displays the library message given by libMessages.sayArrivingLocally(). The library message displays a message of the form "Bob enters the alley." Note that it might sometimes be desirable to customize the travel connector's describeLocalArrival() method: you could say things like "Bob climbs the stairs up to the loft," or "Bob squeezes through the window into the kitchen," for example.

    BasicLocation.cannotGoThatWay now obtains the message to display from a new property of 'self', cannotGoThatWayMsg. By default, this returns &cannotGoThatWayMsg, which is the library message property for the default message for this case. You can easily override a room's message when travel is not possible in a given direction by overriding cannotGoThatWayMsg to return a single-quoted string with your custom message.
    The class GuidedInTravelState formerly defined each of the sayDepartingXxx() methods separately to show the departing-with-guide library message. Now, the class instead defines only sayDeparting() to display this message. This creates the identical effect by default, since all of the sayDepartingXxx() methods inherited from GuidedInTravelState's base class (AccompanyingInTravelState) simply call sayDeparting(). The advantage of this change is that it allows GuidedInTravelState's travel message for all of these different variations to be customized by overriding sayDeparting(), rather than overriding each sayDepartingXxx() individually.
    SpaceOverlay now checks the "moved" status of its "identity" object, not its own "moved" status, to determine if it should abandon its contents on being moved for the first time. This makes the operation work properly when the overlay is a component of a ComplexContainer, which will usually be the case; since only the parent complex container is actually marked as moved, checking the status of the child overlay object wasn't enough. Checking the identity object ensures that the status of the larger object of which the overlay is a part is properly taken into account.
    The TravelPushable method beforeMovePushable() now takes a new third parameter, 'dest', giving the destination of the travel.
    SpaceOverlay now overrides beforeMovePushable() to call abandonContents(). This ensures that if the object is a TravelPushable, and it's pushed to an new location, the contents are abandoned before the actor moves to the destination location. In the past, the object waited for the normal moveInto() notification to abandon its contents; but this notification didn't arrive until after the actor had already moved to the destination location, so the actor wasn't able to see the contents being abandoned, thus we weren't able to list the contents properly.

    ComplexContainer also overrides beforeMovePushable(), passing the notification down to any SpaceOverlay components it has. This corrects the same problem for a ComplexContainer that's also a TravelPushable, and which has SpaceOverlay components.

    SpaceOverlay is now more careful about the way it reveals the contents of the object when it's moved. In the past, it simply did an appropriate kind of nested command (LOOK UNDER for an Underside, for example) to generate the listing. This generally showed up before the main report for the action, so when the main report was a simple default report (such as "Taken"), it created confusion by suggesting that the default report somehow applied to the last object listed in the revelation:

       >take box
       Under the box is a rusty can. Taken.
    

    There are two changes to the way this listing is generated.

    First, the listing is no longer just a generic LOOK UNDER (or equivalent). Instead, it uses a new lister, given by the abandonContentsLister property of the SpaceOverlay object. Underside uses undersideAbandonContentsLister by default, and RearSurface and RearContainer use rearAbandonContentsLister. These new listers use more specific language to describe the revelation, of the form "Moving the box reveals a rusty can underneath" (or "behind").

    Second, the SpaceOverlay takes care to move the listing so that it follows the main report from the command that triggered the listing. This ensures that the main report remains close to the command line, which is required if the terse phrasing of the default reports is to make sense.

    There's one exception, though: if the actor performing the command ends up in a different location at the end of the command, the listing is not moved to the end of the report. Instead, the listing simply stays at the beginning of the command. This exception is important because travel usually triggers a description of the new location, and once we're in the new location, the revelation of the SpaceOverlay's contents will seem out of place. It's better in these cases to leave the contents listing at the start of the reports.

    With these two changes, the old listing above is transformed into this:

       >take box
       Taken. Moving the box reveals a rusty can underneath.
    
    If all of the changes described above to SpaceOverlay's revelation listing are still not adequate for some special case in your game, you can now easily suppress the default listing and generate your own explicit listing instead, thanks to a couple of new features.

    First, the new SpaceOverlay property 'neverListOnMove' lets you suppress the default listing; just set this property to true, and no listing will ever be generated when the overlay object is moved. If the contents are to be abandoned, the abanonment will still happen, but no automatic mention will be made of it. Your code is responsible in these cases for generating any desired listing.

    Second, the new method listContentsForMove() generates the revelation of the contents in the default format. SpaceOverlay calls this method itself to generate the automatic message when appropriate. If you set neverListOnMove to true in order to suppress the default revelation listing, you can call listContentsForMove() at the appropriate juncture in your own code to generate a substitute listing. Of course, you could instead completely customize the listing with your own method; but if you only want to change where the listing appears, without changing its contents, listContentsForMove() makes that easy.

    Note also that you can customize the standard listing format used by providing your own lister, and setting the SpaceOverlay object's abandonContentsLister to refer to your custom lister.

    In the default Floor class, there were a couple of problems with the commands SIT ON FLOOR, STAND ON FLOOR, and LIE ON FLOOR that have now been corrected. First, when standing in a nested room, STAND ON FLOOR incorrectly failed with the message "You're already standing." Second, when already sitting, lying, or standing on the floor, another command to do the same thing was incorrectly accepted; it's now rejected with an appropriate message (such as "You're already sitting on the floor").
    If the player character was in a nested room, and typed a command to leave the nested room, and immediately followed with an AGAIN command, the AGAIN incorrectly succeeded, making it look like the character was getting out of the nested room repeatedly without getting back in. This has been corrected; an appropriate message ("you're not in the booth") is now displayed on the errant repetition.
    In ActorTopicDatabase.initiateTopic(), the conversation is "noted" only if an InitiateTopic is found to handle the request. Noting the conversation means that we set the actor's last interlocutor to the player character, so this change means that initiateTopic() won't affect the actor's interlocutor unless a conversation is actually initiated. (In the past, the routine always noted the conversation. This caused odd side effects when initiateTopic() was called speculatively, without knowing for sure that an InitiateTopic was actually available, since the interlocutor changed even though no conversational action was visible to the player. This change ensures that the interlocutor will only change when a conversation actually takes place.)
    When an InConversationState object becomes the active state for an actor, it remembers the previously active state so that it can, by default, transition back to that previous state when the conversation ends. In the past, the InConversationState class remembered any previous state and used it as the new state at the end of the conversation. This has been changed. Now, the class remembers the previous state only if the previous state is a ConversationReadyState, or there's no other previous state already remembered. This change makes it easier for an actor to interrupt a conversation with another activity, and later resume the conversation, because the interrupting state will no longer "take over" at the end of the conversation. You can still explicitly set the end-of-conversation state explicitly to anything you want; this change only affects the default state transitions that occur in the absence of any explicit settings.
    The HELLO and TALK TO commands now defer the implied topic inventory until the end of the turn, if the response to the HELLO or TALK TO contains any non-default reports. This is important because a non-default report could set the ConvNode for the responding actor, but such a change doesn't take effect until the transcript is fully processed at the end of the turn. In the past, HELLO and TALK TO always showed the topic inventory right away, so the inventory didn't necessarily match the new ConvNode's active topics. With this change, a ConvNode change within a HELLO or TALK TO will be properly reflected in the topic inventory.
    The new ConvNode property 'isSticky' lets you create a "sticky" conversation node. By default, nodes are non-stick: this yields the traditional behavior, where the NPC leaves the node if a response doesn't explicitly stay there. (If the node is active, and the NPC shows a response that doesn't set any new node, the actor's current node is set to nil.)

    If you set 'isSticky' to true for a node, the node will stay active if the NPC shows a response that doesn't set a new node. In other words, the "default next node" for any response while the sticky node is active is the sticky node itself, rather than nil.

    Sticky nodes are useful in cases where you want an actor to drive a conversation along a particular thread, but you still want to allow the player to digress by talking about other topics. As long as a digression doesn't explicitly set a new node, the sticky node will remain active.

    DefaultCommandTopic now has a matchScore of 3, which makes the library choose it over a DefaultAnyTopic if both kinds of default topics are active for a given response. (In the past, DefaultCommandTopic had the same matchScore as a DefaultAnyTopic, so in cases where both kinds were active the library chose one arbitrarily. The DefaultCommandTopic should always take precedence, as it now does, because it's the more specific kind of response.)
    AltTopic now takes its setting for the 'impliesGreeting' property from its enclosing topic. This ensures that an AltTopic used within a HelloTopic or the like will behave the same as its enclosing main topic by default.
    ByeTopic and ImpByeTopic now work together the same way that HelloTopic and ImpHelloTopic work together. Specifically, ByeTopic is now a catch-all that matches both explicit GOODBYE commands and implied conversation endings. (Implied goodbyes happen when the player character just walks away from a conversation, or the NPC gets "bored" from lack of attention). ImpByeTopic still matches only implied goodbyes; since it's the more specific of the two, it now uses an elevated matchScore. This means that if both a ByeTopic and an ImpByeTopic are active at the same time, the ImpByeTopic will be chosen for an implied goodbye, and the ByeTopic will be chosen for an explicit GOODBYE command.

    This change makes it easier to program catch-all goodbye messages, while still allowing differentiation when desired. To create a catch-all message that handles both explicit and implicit goodbyes, just create a ByeTopic. If you want to differentiate, create one of each: the ByeTopic will be chosen for the explicit GOODBYE commands, since the ImpByeTopic won't match those; and the ImpByeTopic will be chosen for implied goodbye, because both will match, but the ImpByeTopic's higher matchScore will ensure that it's selected over the ByeTopic.

    Along the same lines, HelloGoodbyeTopicObj now matches implied as well as explicit goodbyes. (In the past, it handled implied and explicit greetings, but only explicit goodbyes.)

    Some of the behavior of InConversationState.endConversation has been moved to the base ActorState class. This ensures that the active ConvNode will be notified if the player walks away from a conversation.
    The library messages for scoring (showScoreMessage, showScoreNoMaxMessage, showScoreRankMessage, showFullScorePrefix) now refer explicitly to "you", not to the player character. Since the score is a meta-game feature, it should be attributed directly to the player, not to the player character. This is especially important in games that refer to the player character in the first or third person, since the distinction between player and player character is more evident in these cases than it is in second-person games.
    The library message for trying to put an object inside an another object that already contains the first object (i.e., the "circular containment" message) now reports the objects as specified, rather than possibly showing an intermediate container. In the past, an intermediate container was shown in some circumstances, especially when a ComplexContainer was involved; this produced the wrong messages in some cases.
    In the past, the multiple object announcement ("red book: Taken") was missing in in some cases where the action was remapped (via remapTo). This has been corrected.
    The implied action message generator now allows for "silent" implied actions - implied actions that behave as usual but generate no announcement text (the normal announcement is the message of the form "(first opening the door)" shown at the start of the response to the command).

    First, the new libMessages method silentImplicitAction() can be used in place of the standard announceImplicitAction() to generate the announcement. silentImplicitAction() simply shows no message. To use it, perform the implied action with a call like this:

       tryImplicitActionMsg(&silentImplicitAction, Open, self);
    

    Second, the lister that aggregates a set of implicit actions into a single announcement message now accepts the empty messages generated by silentImplicitAction(). This ensures that silent implied actions can be mixed with announced implied actions in a single command, and a sensible aggregate announcement will be displayed.

    Thing.setContentsSeenBy() incorrectly marked the object's contents as having been seen by 'gActor' rather than by the actor passed as a parameter to the method. This has been corrected.
    In PresentLater, when the object is made present (via the makePresent method, or any of the related methods), the object and its contents are specifically marked as seen by the player character, to the extent the player character can actually see them at the moment they become present in the location. In most cases, when an object comes into play dynamically via the PresentLater mechanism, the sudden appearance is the result of some specific event that the game will describe through custom handling - that is, the game will specifically mention the new object to the player, so the player character should remember having seen the object.
    In a two-object command (such as PUT VASE ON TABLE), the parser now retains the meaning of a pronoun used in the command, regardless of which object slot the pronoun is used in. For example, after the command PUT VASE ON IT, the pronoun "it" is set to refer to the indirect object, even though "it" would normally refer to the direct object after a PUT X ON Y command.

    In the past, the parser unconditionally set the pronoun antecedent after a two-object command to the second-resolved object, which in many such verbs is the direct object. So, after PUT VASE ON IT, the pronoun "it" previously referred to the vase. Due to this change, though, the explicit use of the word "it" directly within the command causes meaning of "it" to be left unchanged by the command. This change doesn't affect the behavior when no pronoun is used in the command, so PUT VASE ON TABLE still causes "it" to refer to the vase on the subsequent command.

    ConsultTopic and DefaultConsultTopic now refrain from setting any pronoun antecedents for the topic. This means that after any consultation command (LOOK UP, FIND IN, etc.), the pronoun antecedent will be the consultable object, not the topic last sought.
    TopicTAction (used for commands such as ASK ABOUT and CONSULT ABOUT) didn't work properly if a reflexive pronoun was used as the direct object, referring back to the topic phrase: LOOK UP BOOK IN ITSELF, for example. This showed a couple of symptoms, including "Nothing obvious happens" responses and run-time errors. The problem has been corrected; the action now looks to the topic phrase to resolve the direct object, producing consistent and appropriate results.
    When a command is directed to an NPC (as in BOB, OPEN DOOR), and the parser has to prompt interactively for disambiguation or for a missing noun phrase, the parser now treats gender-matched third-person pronouns in the reponse as referring to the target actor. For example:

       >look at bob
       He's wearing a red sweater.
    
       >bill, examine
       What do you want Bill to look at?
    
       >his book
       The book looks old and dusty; the title on the cover is faded
       to the point of being unreadable.
    

    In the past, the parser would have taken the "his" in the interactive response to refer to Bob, since Bob would have been the pronoun antecedent up until that point (by virtue of having been mentioned in the previous command). With this change, the parser takes "his" to refer to Bill. This fits with the phrasing of the prompt text, and also with the fact that Bill was mentioned more recently in a command (specifically, in the incomplete "examine" command).

    Similarly, reflexive third-person pronouns (HIMSELF, HERSELF, ITSELF, THEMSELVES) in interactive responses are taken as referring to the target actor, when the gender matches.

    The parser is now more consistent in its treatment of truncated vocabulary matches. (Truncated matches are words in the player's command that match longer object names defined in the game, by truncating the longer object names to the dictionary's minimum length. For example, the default English dictionary truncation length is 6 characters, meaning that the player can shorten the word FLASHLIGHT to FLASHL, FLASHLI, etc., when entering commands.)

    In the past, the parser treated exact matches as stronger than truncated matches for singular definite noun phrases (the most common kind of noun phrase: TAKE BOOK, OPEN THE BOX). Any time the parser matched some names exactly and other names only after truncation for the same user input, the parser kept only the exact matches, filtering out the truncated matches. This rule hasn't been changed; what's changed is that it has now been extended to every kind of noun phrase, including indefinite phrases (TAKE A BOOK) and plural phrases (TAKE BOOKS).

    In the English parser, when the parser matches a plural phrase (i.e., a phrase with a vocabulary word defined under the 'plural' property), the parser now includes both 'plural' and 'noun' matches for the word. In the past, only the 'plural' matches were included.

    In some cases, it's convenient to define a word that's grammatically a plural under the 'noun' property, because a single game object encompasses what looks to the user like a set of something: a bookcase might define "shelves" as a noun, for example, since the individual shelves aren't modeled as separate objects. This change ensures that if other objects are in scope that define the same word under the 'plural' property, the bookcase will still match the word "shelves" when the noun phrase is resolved.

    This change doesn't actually make much difference most of the time, but it is frequently significant in "topic" phrases (as in ASK ABOUT). Topic phrases have very wide scope, so a particular word can match numerous objects from all over the game. In these cases, the old scheme that included only 'plural' matches was overly exclusive, causing the parser to omit objects from the topic resolution that the author would at first glance have expected to be included. The new more inclusive matching should reduce the potential for confusing match exclusions in these cases.

    The English parser is now more inclusive in matching nouns and adjectives at the ends of "topic" phrases. (Topic phrases are used in commands like ASK ABOUT and TELL ABOUT, where the player can refer to physical objects as well as to abstract Topic objects, and can even use random text that doesn't correspond to any game objects.)

    Normally, when the last word of a noun phrase can match one or more objects in scope that define the word under the 'noun' property, the parser will ignore any additional in-scope objects that match the same word under the 'adjective' property. For example, if there's a desk in scope that defines 'desk' as a noun, and there's also a desk drawer that defines 'desk' as an adjective, the parser will interpreter LOOK AT DESK as referring to the desk, not to the drawer. However, if there's a red book present, but there's no object in scope that defines 'red' as a noun, the parser takes READ RED as referring to the red book, even though BOOK was left unstated. This combination of rules gives the player the convenience of abbreviating the name of an object to a unique adjective, while avoiding spurious ambiguity in cases like the desk-and-drawer exmaple.

    In cases of "global" scope, though, this strategy isn't as beneficial. When resolving a topic phrase, objects from all over the game have to be considered "in scope" even though they're not physically present, as it's perfectly reasonable for the player to ASK ABOUT something that was seen earlier in the game but isn't physically present at the time of the question. As a result, the elimination of adjective bindings for a word can cause subtle problems that are very difficult to track down, and which sometimes can't be easily fixed without creating other problems. Two objects could happen to share the same vocabulary words, in one case as an adjective and in the other case as a noun, even though the objects are completely unrelated. Thus, the existence of one object can change the way another object's vocabulary words are treated when matching a topic phrase.

    The English parser now treats topic resolution differently. When resolving a topic phrase, the parser no longer makes the strict noun/adjective distinction. Instead, the parser considers both kinds of vocabulary to match. So, if the player types ASK BOB ABOUT DESK in our example with the drawer, both the desk and the drawer are now considered possible topic matches.

    This change has the potential to create a different sort of problem, in that it could include too many possible matches for a topic phrase. However, this is a much easier problem to deal with than the old one, and in most cases won't even be noticeable as a problem. In the first place, topic phrases don't require disambiguation in the normal sense; the parser never asks the player which object was intended, so the more inclusive matching won't degrade the user interface by generating more disambiguation queries. Second, it's much easier to understand why an extra object is matching a given vocabulary word than to understand why a given object is failing to match. A failure to match indicates that some other object is defining the same word under another part of speech, but it doesn't tell you which object or where it's defined; when too many objects match, in contrast, you can easily see (using the debugger, for example) which extra objects are matching, and make any desired vocabulary adjustments. Third, in the vast majority of cases, the extra matches are completely harmless and not even noticeable. Topics are generally handled in conversations, and conversations have to explicitly include responses for all of the topics they want to match. If an extra object or two show up in vocabulary matching for a given conversational command, it won't usually matter, because those unexpected objects won't have topic matches in the conversation anyway and will thus simply be ignored. In cases where you want a conversation to distinguish among objects with similar vocabulary, you can add the necessary distinguishing vocabulary to the objects just as you would in cases of ordinary ambiguity, and you can tweak the topic list (using match scores, for example) to control which response is used for which vocabulary. In the rare cases where it's not straightforward to fine-tune the matching using object vocabulary, you can always resort to using regular expressions as the topic match criteria.

    Consider a situation where a given object is entered in the dictionary with two words for a given part of speech, and one of the words happens to be a leading substring of the other word, and both words are long enough to trigger "truncated" matching (that is, each word is at least six characters long, in the default dictionary truncation configuration). For example, suppose a given object is entered in the dictionary with 'noun' words for WINDOW and WINDOWSILL. Now, if the player enters a command using the shorter word (WINDOW), the raw dictionary match list will contain two matches for this object: one for the exact match to the shorter word, and one for the truncated match to the longer word.

    In the past, this situation sometimes had undesirable results. In most cases, the two matches to the same object were collapsed into a single match, but the "match flags" from the two matches were combined - so both matches were marked as truncated. Depending on what other objects were in scope at the time, this had the effect of eliminating the object from consideration as a resolution of the noun phrase: even though it had an exact match in the dictionary, the resolver saw it as a truncated match because of the combination of the two sets of match flags. This was especially likely to affect topic phrases (ASK ABOUT, for example), because the global scope made it much more likely that other objects would have exact matches for the same vocabulary.

    The parser is now smarter about the way it combines these sorts of redundant matches. Instead of simply OR'ing together the match flags, the parser now specifically keeps only the "strongest" match in these cases. In our example, the parser sees that there's a truncated dictionary match (WINDOWSILL) and an exact match (WINDOW) for the same object, so it now decides to consider the match to be exact.

    The English parser now accepts IN object, INTO object, and IN TO object as verb synonyms for ENTER object. These aren't exactly valid English grammar, but they have a certain linguistic consistency with other telegraphic IF-ese constructs, and they seem to naturally occur to some players. These variations all have an obvious meaning to an English speaker, and they're not ambiguous with any other commands, so there should be no harm in adding them as convenient abbreviations.
    After a misspelling, if the player typed OOPS but left off the corrected spelling (i.e., just typed OOPS by itself on the command line), the parser responded with a message saying that OOPS isn't allowed right now. This has been corrected; the parser now responds with a new message explaining the correct syntax, and gives the user another chance to enter the OOPS command.
    When a single player command triggers more than one implied action (for example, GO NORTH might trigger OPEN DOOR, which in turn might trigger UNLOCK DOOR), the reporting mechanism had a couple of problems when the doubly-nested action (UNLOCK DOOR in our example) itself triggered a nested action, such as by remapTo or nestedAction. First, the reported order of the implied actions was reversed ("first opening the door, then unlocking it"); second, the doubly-nested one reported the nested action rather than the original implied action. Both of these have now been corrected.
    When you synthesize an action programmatically (via a function such as replaceAction or nestedAction), the library has to choose a language-specific final subclass of the generic action you specify. It does this by choosing a VerbRule that's subclassed from the base action. In the past, the choice was entirely arbitrary. Now, the library is a little pickier: it now chooses only a final subclass, never an intermediate class. That is, it always chooses a VerbRule which hasn't been further subclassed (or modified with 'modify'). The old, less selective algorithm occasionally caused oddities, such as choosing a base VerbRule that was subclassed with 'modify', thus picking up pre-'modify' values of properties such as the verb's descriptive text.
    The conversationManager object now provides a simple extensibility mechanism that lets you add your own custom conversation tags. To add custom tags, use 'modify' to extend the conversationManager object, then add two things. First, define the customTags property, giving a string with the new tag names you want to add. The tag names must be in lower-case letters; do not include the angle brackets or the leading period in the tag name. Separate multiple tag names with "|" symbols. Second, define a doCustomTag(tag,arg) method that processes your custom tag or tags. The 'tag' argument is a string giving the custom tag that was matched in the output stream; only the tag name will be included, not including the angle brackets or leading period. The 'arg' method is a string giving the argument to the tag, if any; this is simply the part inside the angle brackets following any whitespace after the tag.
    SensoryEmanation and its subclasses Noise and Odor will now let you use Script objects for the hereWithSource and hereWithoutSource properties. This is handy in cases where you want to vary the object's description over time. If you define these properties as Script objects, the SensoryEmanation object will invoke their doScript() methods when an emanation message is to be displayed.
    The Keyring class has a new method, getLooseKeys(actor), that identifies the loose keys in an actor's possession that are eligible for automatic attachment when the keyring is taken. By default, this returns the actor's direct contents (i.e., the objects the actor is directly holding).

    This can be overridden to consider, for example, loose keys held in the actor's pocket. To prevent the keyring from automatically grabbing any keys when it's taken, simply override this method to return an empty list. Note that you don't need to bother limiting the returned list to include only valid keys, since the caller does this by considering only objects for which isMyKey() returns true.

    The function withParserGlobals() now takes an additional parameter giving the actor who issued the command. (This is an internal function that's probably not used at all by existing game code, so it should have no impact on games. If you are calling this function, though, note that you'll need to supply the extra parameter.)
    The menu system now sets the main content banner window to use scrollbars if possible, and automatically scroll to show new text in any case. This ensures that hint lists that are too long to fit in the window will scroll as needed to keep the last hint in view.
    3.0.6o

    Released 3/14/2004

    Possible compatibility-breaking change: The property formerly named 'vocabWords_' has been renamed to 'vocabWords' (the change is that the underscore formerly at the end of the name has been removed). The underscore suffix is conventionally used in the library to indicate an internal library property that isn't normally for use by game code, which obviously isn't appropriate in this case, as this is the property that games usually initialize to set an object's vocabulary words.

    You should search for instances of 'vocabWords_' in your existing code, and replace them with 'vocabWords'. Most existing game code will probably have few, if any, instances of this property name, since most object definitions in existing game code probably use the standard library templates to set this property's value without giving its name. Even so, you should search through your code and update any mentions to use the new name.

    The method getOutermostRoom(), which was previously defined in BasicLocation and NestedRoom, has been moved instead to Thing. This ensures that the method will work properly even when a nested room is located within a non-room object. The Thing definition simply returns the enclosing location if there is one, or 'self' if not.
    The new TopicEntry subclass AskTellGiveShowTopic makes it easy to define a response that covers ASK, TELL, GIVE, and SHOW for a given object or set of objects. It's sometimes convenient to be able to treat all of these commands as equivalent in handling a response.

    AskTellGiveShowTopic is based on the new class TopicOrThingMatchTopic, which can be subclassed to create other combinations of other subsets of these four verbs (or to add topic entries for new custom game-defined verbs). This new class combines the features of the "Thing" and "Topic" base classes for topic entries.

    ConvNode has a new method, autoShowTopics(), that returns true if the node is to show a topic inventory automatically on activation. By default, this method returns true if the node contains any active SpecialTopic entries. You can override this if desired to generate a topic inventory automatically under other conditions, or to suppress the automatic inventory when an active special topic is present (although the latter probably isn't a good idea).

    The main reason you might want to generate a topic inventory automatically on activating a node is that the node contains one or more obscure topics that players will be unlikely to guess on their own. If it's unlikely that the player would think to ask just the right thing at just the right time, a topic inventory can help guide them through the conversation by showing the possibilities. As always, only the topics you specifically mark as suggested (with SuggestedAskTopic, etc) will be listed in the inventory. When possible, it's probably better from a game-design perspective to lead players into asking the right questions more subtly, by putting the right cues in the conversation itself; when this is impractical, the topic inventory is a usable substitute.

    The pronoun expansion for CollectiveGroup objects that was introduced in 3.0.6n has been partially removed. In the past, when an individual object was associated with a CollectiveGroup object, the parser automatically added the group object into consideration for resolving a subsequent pronoun referring to the individual. This was meant to provide better fidelity for resolving pronouns in these cases, but it led to new problems when the original noun phrase could only have referred to the individual. To correct the problems, the parser no longer puts the collective group into consideration when a pronoun refers to one of the group's individuals.

    The other direction still applies, though: when a pronoun refers to a CollectiveGroup object, the parser puts the group's associated individuals into consideration for resolving the pronoun.

    A bug in the CommandTopic class (introduced in 3.0.6n) prevented it from matching a single Action class as the matchObj correctly. (It did work properly when a list of Action classes was given, but not when a single object was given.) This has been corrected.
    The HelloGoodbyeTopic class now has its impliesGreeting property set to true, ensuring that this topic type won't trigger an implied greeting. Since a HelloGoodbyeTopic is itself a greeting, an implied greeting would be undesirable, not to mention that it caused a run-time error when it occurred.
    A bug in ComplexContainer caused a problem when an object was initially located in one of the the complex container's sub-containers via the 'subLocation' property. In the past, the ComplexContainer didn't properly re-initialize the object's 'location' property to reflect that it was actually in the sub-container. This has been fixed.

    The problem manifested as strange behavior when the contained object was removed from the sub-container (via TAKE or the like). In particular, because the object's 'location' was still set to the ComplexContainer, the object wasn't properly removed from the sub-container's contents list, so the object appeared to still be in the sub-container as well as in its new location.

    A couple of problems involving formatting and real-time events have been fixed. In 3.0.6i, fuses and daemons - including the real-time versions - started running with a transcript context, which means that their text output is captured. This caused some interactions with the input manager that weren't taken into account properly in the library.

    First, if a real-time event itself stopped and asked for input from the user (by reading a new command line, for example), the transcript capturing prevented the text displayed by the event from showing up at all until after the user entered something on the new command line. It was possible to work around this by deactivating the transcript prior to showing any output in the real-time event, but this is no longer necessary.

    Second, even if a real-time event didn't display anything, its mere silent firing caused an extra blank line to show up immediately after the user next pressed the Enter key to finish a command line.

    Both of these problems are now corrected, so there's no need for real-time events to do anything special with respect to output formatting or command-line input.

    The new pre-defined message object parameter 'pc' can be used to refer directly to the player character. This is similar to the 'actor' parameter, but refers to the player character actor even if an NPC is the current active character. An expression such as "{you pc/he}" thus refers to the current gPlayerChar, not to the current gActor.
    The library messages for scoring (showScoreMessage, showScoreNoMaxMessage, showScoreRankMessage, showFullScorePrefix) incorrectly referred to the current actor rather than the current player character, which resulted in attributing the score to an NPC rather than to the player if a score message was generated on another actor's turn. These messages now explicitly refer to the player character.
    3.0.6n

    Released 3/6/2004

    Major compatibility-breaking change: The library's main entrypoints to the game have changed. Existing games must be modified to accommodate this change.

    In the past, the game code was responsible for defining two entrypoints: main() and mainRestore(). The template games generated by Workbench's "New Project" command defined these two functions as one-liners that called mainCommon(), where the real action took place.

    In the new arrangement, the library now invokes methods of a new object, gameMain, which each game must define. For your convenience, the library defines a class, GameMainDef, that you can use as the base class of your gameMain object. This base class defines suitable defaults for the required methods, so you only have to override what you need to customize. Overall, this change should result in much less required startup code for each game.

    If you use GameMainDef as the base class of your gameMain object, you're only required to define one property in it:

    In addition, there are a couple of optional optional methods that you can use for customization:

    Most games will find it sufficient to define only initialPlayerChar and showIntro(), and simply inherit the default handling for everything else from GameMainDef. However, there are several additional methods that you can override if you want to do something special.

    If your existing game code uses the Workbench-generated template, or you follow the same pattern, then rewriting your code to fit the new design is relatively straightforward. Just follow these steps.

    That's it - if you were using the standard Workbench template for mainCommon(), you're now finished.

    If you made certain kinds of changes to the template main(), mainRestore(), or mainCommon() that Workbench produced when you originally created your game, you might have to do some additional work. These extra changes are uncommon, but check this list to make sure they don't apply to you:

    Minor Compatibility-breaking change: A few properties that were previously in libGlobal and other library objects have been moved into the new gameMain object instead. The relocated properties all select optional library behavior; they've been moved to gameMain to make it easier for game authors to select non-default option settings. In the past, you had to use 'modify' to change these settings, or explicitly assign them from your startup code; now, you simply have to include any non-default option settings as property values in your gameMain object definition.

    The properties moved are:

    Compatibility-breaking change: The format of the verbPhrase string has changed slightly for the English version of the library. Game-defined verbs might need to be adjusted to the new format.

    The change is that prepositions in a verb phrase are now grouped inside the parentheses of the "(what)" placeholders for the objects, if the prepositions go with the noun phrases. For example, the LookIn action's verbPhrase string has changed from 'look/looking in (what)' to 'look/looking (in what)'. The difference is subtle: by moving the preposition 'in' so that it's inside the parentheses with the direct object's "(what)" placeholder, we're telling the library that the 'in' is part of the direct object noun phrase. Contrast this with the verbPhrase for Doff, which is now 'take/taking off (what)': this tells us that the preposition 'off' is part of the verb structure, not part of the direct object.

    How can you tell which way it goes? There are two rules you should use to determine this.

    First, if the preposition separates the direct and indirect objects, such as the IN in PUT X IN Y, then it always goes inside the indirect object phrase, hence 'put (what) (in what)'.

    Second, for any other preposition, use the "it test": try writing the verb using 'it' in place of 'what', and ask whether it sounds right for the preposition to go before or after the 'it'. For example, for the Doff action, TAKE IT OFF sounds right, and TAKE OFF IT is obviously wrong. This means that the preposition 'off' belongs in the verb phrase, hence 'take off (what)'. In contrast, for the LookIn action, LOOK IN IT sounds right, not LOOK IT IN, so the preposition goes with the direct object, hence 'look (in what)'. If the "it test" indicates that the preposition goes before the 'it', then the preposition is part of the direct object and thus goes inside the parentheses of the "(what)"; if the preposition goes after the 'it', then the preposition is part of the verb and goes outside the parentheses.

    Prepositions that go with the verb are uncommon in two-object verbs, but they do occur. An example from the library is ConsultAbout, one form of which uses the verbPhrase 'look/looking up (what) (in what)'. We'd write LOOK IT UP IN IT, so the preposition 'up' goes after the direct object pronoun and is thus part of the verb, while the preposition 'in' goes before the indirect object pronoun and is thus part of the indirect object. In case there's any ambiguity about whether the 'up' goes with the verb or with the indirect object, try the un-it test: we'd write LOOK UP TOPIC IN BOOK, so clearly the 'up' is not part of the indirect object, but is just part of the verb.

    Compatibility-breaking change: The TextList classes have been removed, leaving only the more general EventList classes. The TextList classes have been gradually converging with the EventList classes anyway, to the point where the TextList classes have become nothing more than renamed versions of the corresponding EventList classes. Retaining the parallel set of names for otherwise equivalent classes is bad because it steepens the learning curve for new users and makes for more to remember for experienced users; so, the redundant names have now been dropped.

    Existing code will need to be scanned for occurrences of TextList, StopTextList, RandomTextList, ShuffledTextList, and SyncTextList. Replace these names with CyclicEventList, StopEventList, RandomEventList, ShuffledEventList, and SyncEventList, respectively. (Note that you can simply do a global search-and-replace to change to suffix TextList to EventList, except that you must change occurrences the TextList when it occurs as an entire word to CyclicEventList.)

    In addition, the following property names must be changed:

    If you really don't want to change your existing code, you can always use #define to create macros with the substitutions. You really should change your code if possible, though, since using the old names is likely to create confusion as the old names recede from your memory and from the documentation.

    Compatibility-breaking change: In ConversationReadyState, the greeting and goodbye messages have been changed.

    First, the greetingList property is no longer used; instead, the state now looks for a HelloTopic entry and uses its response. In addition, you can differentiate between explicit HELLO commands and implied greetings generated by other conversational commands (ASK TO, for example) by creating separate HelloTopic and ImpHelloTopic objects. In most cases, it's not necessary to distinguish the two cases, so you can simply use a single HelloTopic to cover both cases.

    Second, the enterFromByeMsg and enterFromConvMsg methods have been removed, along with the enterFromByeList and enterFromConvList properties. Instead, the state now looks for a ByeTopic or ImpByeTopic entry, depending on whether the conversation is ending due to an explicit GOODBYE command or due to an automatic ending (due to the other actor walking away, or due to an inactivity timeout) and uses its response.

    You should scan your existing game code for any greetingList, enterFromByeMsg, enterFromByeList, enterFromConvMsg, and enterFromConvList properties, and change them to HelloTopic, ByeTopic, and ImpByeTopic as appropriate.

    Note that both the HELLO and GOODBYE topic entries go in the ConversationReadyState, not in the InConversationState. This is because these handlers typically describe the transition to and from the "ready" state. A single in-conversation state could work with several "ready" states, so putting the hello/goodbye topic entries in the in-conversation state wouldn't allow any differentiation among the several "ready" state transitions. Keeping the hello/goodbye entries in the "ready" states themselves makes this differentiation easy. For example, this allows a "goodbye" message to say something like "Bob goes back to sweeping the porch."

    Possibly compatibility-breaking change: The internal methods that handle conversational commands in Actor have changed. The changes are isolated to the internal methods that route the commands within and between the Actor and ActorState objects, not to the main public interfaces that game code usually uses. Even so, since it's occasionally useful to override these internal methods, some existing game code might be affected.

    In general terms, these changes are designed to concentrate the handling of all conversational actions along a single path. In the past, each type of conversational command was handled by its own cluster of methods, so the Actor and ActorState had a set of parallel methods for HELLO, GOODBYE, YES, NO, ASK ABOUT, ASK FOR, TELL ABOUT, SHOW TO, and GIVE TO. This old design was intended to make it easy to override these handlers in isolation, but experience has since shown that it's much more useful to be able to override all of the handlers in concert instead. The new design therefore consolidates all of these different conversational actions into a single method that's parameterized with the type of action. There are still several methods because of the sequence of processing points through the Actor and ActorState, but there are no longer several clusters of methods: each of the old clusters is now a single method.

    An additional benefit of these changes is that HELLO and GOODBYE are now handled through the topic database like everything else. This has two useful effects. First, it means that a DefaultAnyTopic entry will now respond to a HELLO or GOODBYE along with everything else. This is highly desirable in most cases, because these defaults are usually meant to convey a blanket response to all conversational overtures. Second, it means that it's now a bit easier to customize HELLO and GOODBYE responses: just use HelloTopic, ByeTopic, and HelloByeTopic objects as desired.

    Advice: Because there are a lot of details to these changes, we suggest you scan your code for mentions of the affected methods, to see if your code is affected at all. If your code doesn't define or call any of these methods, you shouldn't be affected. The methods are:

    Now to the specific changes.

    Actor.sayToActor() has some changes to its parameters. The 'prop', 'propArgs', and 'topicListProp' parameters have been dropped, and the new 'topic' and 'convType' parameters have been added. 'topic' is a special object representing the topic; the library defines the singletons helloTopicObj, byeTopicObj, yesTopicObj, and noTopicObj for the corresponding library actions. 'convType' is an object of type ConvType describing the type of action being performed.

    The new helloTopicObj and byeTopicObj singletons have been added, as mentioned above. These are used as special topic placeholders for the HELLO and GOODBYE commands, respectively.

    The new ConvType class has been added, and singleton instances for all of the standard library conversation actions (HELLO, GOODBYE, YES, NO, ASK ABOUT, ASK FOR, etc.) have been defined.

    The 'topicListProp' and 'handler' parameters of ActorState.handleConversation() has been dropped, and the new 'convType' parameter has been added. 'convType' is a ConvType object representing the conversation type.

    The 'topicListProp' parameter of ConvNode.handleConversation() has been replaced with a new 'convType' parameter, which is a ConvType object describing the conversation type.

    Actor now provides a handleConversation() method. The ActorState will invoke this method by default when the ActorState's own handleConversation() method doesn't handle the action. This new Actor method is in lieu of the former cluster of per-command handlers in Actor: yesnoFrom, answerQuestion, hearAbout, beShown, beGiven, answerRequestFor.

    All of the per-command handlers in Actor and ActorState have been removed: yesNoFrom, answerQuestion, hearAbout, beShown, beGiven, answerRequestFor. These are replaced by the handleConversation() method in Actor and ActorState, as mentioned above.

    The new Actor method defaultConvResponse() is a general handler for showing the default response when a conversational action isn't otherwise handled (that is, it's not handled by any topic database entry, and the ActorState doesn't want to handle it specially). This new method provides an easy way to show the same response for every conversational action - just override this one method, and you can show a common response for all conversation commands. By default, this method routes looks at the convType parameter to determine the conversation type, and invokes the Actor method that provides the default response for that particular conversation type, using the same default response methods used in the past (defaultGreetingResponse, defaultGoodbyeResponse, etc).

    The ActorState object can now optionally define any of the default response handlers that Actor can define (defaultGreetingResponse, defaultAskResponse, etc). When these are defined, they'll be called when the state object doesn't provide a suitable TopicEntry in its topic database. Note that these effectively override the Actor's topic database and default response handlers for a given type of action. The order of handling for a conversational action is now ConvNode, ActorState TopicEntry list, ActorState default response method, Actor TopicEntry list, and finally Actor default response method.

    Possibly compatibility-breaking change: A change to the library's message system makes it easier to customize the default response messages for individual objects. In the past, if you wanted to customize the message for OPEN COCONUT, say, you'd have to override the 'verify' method for the 'coconut' object's dobjFor(Open). With this change, you can create a custom per-object message more easily: you simply define coconut.cannotOpenMsg with the custom message string.

    You can use the new message customization scheme any time the library generates a standard response message using code like this:

      dobjFor(Open)
      {
        verify { illogical(&cannotOpenMsg); }
      }
    

    Any time you see library code like that, you can override the default response message for that action on an individual object simply by defining the message property in your object:

      coconut: Thing
        cannotOpenMsg = '{You/he} would need something sharp to do that. '
      ;
    

    In addition to illogical(), this also works with illogicalNow(), inaccessible(), defaultReport(), defaultDescReport(), extraReport(), mainReport() reportBefore(), reportAfter(), reportFailure(), and any other verify or action reporting routines.

    Here's how this works. In the past, when a verify, check, or action routine generated a message using a message property, the library looked up the message in the current actor's "action message object," which is usually playerActionMessages when the actor is the player character, and npcActionMessages otherwise. Now, the library still looks there, but only after checking the individual objects involved in the command to see if any of them define the message property.

    The library looks first at the direct object, then at the indirect object. If you were to create your own custom verbs with three or more object slots, such as PUT COIN IN SLOT WITH TWEEZERS, the library would automatically continue on to those extra objects as well. If the library finds the message property in any of these objects, it stops and uses that object as the source of the message; if it can't find the message property among these objects, the library simply falls back on the standard message object (playerActionMessage or whatever).

    Important: As part of this change, all of the library message properties have been renamed to end in "Msg". This affects every message property, so if you've created your own "action message object," or you've used 'modify' to change playerActionMessages and/or npcActionMessages, you'll have to do some extensive searching and replacing to add the "Msg" suffix to every message property name. Sorry about this; the proposed change was put to the TADS 3 mailing list, and no one objected. The naming change isn't gratuitous. The reason for the name change is that it should greatly reduce the chances of collisions between message properties and properties defined for internal use by an object. The library itself formerly had a number of these collisions, so it was necessary to rename at least those properties; using the naming convention consistently for all of the message properties will help ensure that games don't inadvertantly introduce their own name collisions.

    There's one last detail to mention. An object can override a message property with another message property. For example, the Vaporous object in the library uses this feature:

      notWithIntangibleMsg = &notWithVaporousMsg
    

    When a message property in an object points directly to another property, the library takes this as an indirection to another library message from the action message object. This feature is mostly for the library's benefit, since library objects are required to get all of their messages from the action message object (to ensure that the library can be translated without rewriting entire object definitions).

    Minor compatibility-breaking change: The Script method getState() has been renamed to getScriptState(), and the Script property curState has been renamed to curScriptState. This change allows Script and its subclasses to be combined (with multiple inheritance) with Thing (which defines its own meaning for getState) and with Actor (which defines its own curState). It's sometimes desirable to combine Thing or Actor with a Script subclass, because that's an easy way to attach simple scripting behavior to these objects.

    Existing game code that overrides or calls getState or curState for a Script object (including any EventList subclass) will need to be changed to use the new name. You should scan your source code for occurrences of these names, and rename them as needed. Note that you should not rename curState properties that pertain to Actor objects, since Actor.curState has not been renamed.

    In most cases, game code won't have any reason to override or access these script properties at all, so most game code should be unaffected by this change. Game code usually just defines instances of the library EventList subclasses using templates, and such code won't be affected by this change.

    Minor compatibility-breaking change: The library objects redirectTravelIn, redirectTravelOut, and redirectTravelDown have been renamed to askTravelIn, askTravelOut, and askTravelDown, respectively. The names changes are for consistency with the naming of the new class AskConnector.
    The PresentLater class has a new property, initiallyPresent, that lets you override the standard behavior of the class, which is to make the object initially absent from the game map. This new property is set to nil by default; if you override it to true, the object will be initially present in the game, like any ordinary object. This new property would seem to defeat the purpose of the class, but it actually extends the class to situations where an object comes and goes during the game, but starts out present. Using PresentLater with initiallyPresent set to true, you can still use all of the PresentLater showing and hiding mechanisms, such as the key-based and conditional methods; this is often more convenient than moving objects in and out of the game map individually and manually.
    The Attachable class now automatically notifies itself and each of its attachments whenever the Attachable is carried by a traveler, by calling the new method travelWhileAttached(). The method does nothing by default, but games can override it as needed to enforce conditions or carry out side effects when an attached object is moved indirectly via travel.
    The Attachable class now has a way of specifying the "direction" of an attachment relationship, for the purposes of descriptive messages. Formerly, attachments were always described symmetrically: if A was attached to B, then examining A generated a status message along the lines of "A is attached to B," while examining B generated a message like "B is attached to A." This didn't always work; "the note is attached to wall" is fine, but "the wall is attached to the note" isn't quite right. The new feature lets you specify that the note is always said to be attached to the wall, never vice versa.

    The direction of a relationship is specified by the new Attachable method isMajorItemFor(obj). By default, this method always simply returns nil, which means that there are no "major" items by default, which makes all attachment relationships symmetrical by default. If you wish, you can override isMajorItemFor() so that it returns true in some cases. When A.isMajorItemFor(B) returns true, the relationship will always be described such that B is said to be attached to A: examining A will yield "a B is attached to the A," while examining B will show "the B is attached to an A."

    A few new methods have been added to support the new feature; these are mostly for internal use, but could potentially be used by a game to fine-tune the way attachments are listed. The new method isListedAsAttachedTo(obj) lets the object indicate whether or not 'obj' is listed among the things 'self' is attached to; by default, this returns true if 'obj' isn't permanently attached and 'self' isn't the "major" item for 'obj'. The new method isListedAsMajorFor(obj) is essentially the major-list counterpart: it indicates whether or not 'obj' is listed among the things attached to 'self' when 'self' is described. By default, this method returns true if 'self' is the "major" item for 'obj', and obj.isListedAsAttachedTo(self) returns true (that is, 'obj' thinks it should be listed as attached to 'self'). Finally, majorAttachmentLister returns the lister to use for the items attached to 'self' for which 'self' is the "major" item in the relationship; by default, this uses a MajorAttachmentLister instance.

    The new classes Underside, RearContainer, and RearSurface make it easier to model situations where one object is behind or under another. Underside can be used to model the space under an object, or for the bottom surface of an object. RearContainer models the space behind an object, and RearSurface models its back surface.

    In addition to letting you set up "under" and "behind" relationships among objects initially, these new classes support the PUT UNDER and PUT BEHIND commands to let actors add new contents under and behind the objects. (The PUT BEHIND command is also new in this release.) An Underside can have new objects added under it with PUT UNDER, and a RearContainer or RearSurface can have new contents added with PUT BEHIND. These commands can optionally be disallowed for a given Underside or RearContainer/RearSurface: override the properties allowPutUnder and allowPutBehind, respectively. The new classes derive from BulkLimiter, so you can use the usual BulkLimiter properties to control the individual and total bulk allowed under and behind the objects.

    ComplexContainer has been extended to support these new classes. The new ComplexContainer property subUnderside can be set to an Underside object representing the space under or bottom surface of the complex container; the new property subRear can be set to a RearContainer or RearSurface representing the space behind or back surface of the complex container. PUT BEHIND and LOOK BEHIND commands on the complex container are routed to the subUnderside; PUT UNDER and LOOK UNDER are routed to the subRear; and both subcomponents are included in the regular LOOK AT display.

    These additions were adapted from work originally done by Eric Eve.

    It's now easier to set up objects so that they're initially inside the component sub-containers of a ComplexContainer. In the past, if you wanted to create an object in your source code so that it was initially inside a ComplexContainer's internal container, for example, you had to explicitly set the object's 'location' property, rather than using the "+" syntax. Now, you can use the "+" syntax as long as you add a little extra information: give each contained object a new property, 'subLocation', and set it to the property of the component sub-container you want to use as the initial location. For example, here's how you'd create a washing machine as a complex container, with a blanket inside and a laundry basket on top:

      + washingMachine: ComplexContainer 'washing machine' 'washing machine'
        subContainer: ComplexComponent, Container { /* etc */ }
        subSurface: ComplexComponent, Surface { /* etc */ }
      ;
    
      ++ Thing 'blanket' 'blanket'
        subLocation = &subContainer
      ;
    
      ++ Container 'laundry basket' 'laundry basket'
        subLocation = &subSurface
      ;
    

    The blanket and the laundry basket are nominally directly inside the washing machine itself, according to the "+" syntax, but their 'subLocation' settings ensure that they end up in the desired component sub-containers during initialization.

    Note that 'subLocation' is only intended for initialization, so the library automatically sets subLocation to nil for each object right after the initial setting is used. This helps avoid any unpleasant surprises should the object be moved into a different ComplexContainer later on. When you're moving objects around on the fly in your program code, there's no reason to use subLocation at all; instead, just specify the appropriate component as the explicit destination of the moveInto: towel.moveInto(washingMachine.subSurface), for example.

    The Openable class has a new property, openingLister, that specifies the lister to use to display the list of items revealed when the object is opened. By default, this is set to the openableOpeningLister object. Individual objects can override this if they want to customize the message listing the revealed items.

    In addition, the object formerly called openingLister has been renamed to openableOpeningLister.

    The new CommandTopic makes it easier to generate a response when an NPC receives a particular command. Just create a CommandTopic, with a list of the Action classes you wish to match. Actions are matched on class alone; if you want to match something more specific, such as matching a particular direct object of a particular action, you'll have to create a custom matchTopic() method for your CommandTopic object. If you want to create a default response that matches any action, use a DefaultCommandTopic.

    CommandTopic works like any other TopicEntry object, so you can put these in ConvNode, ActorState, and Actor topic databases to provide responses under the specific conditions you need to match.

    As part of this change, ActorState.obeyCommand() now invokes handleConversation() to look for a response to the command, specifying the Action object as the topic and 'commandConvType' as the conversation type object. This looks as usual in the ConvNode, ActorState, and Actor conversation topic databases for a CommandTopic to handle the response, or for a DefaultAnyTopic if there's no CommandTopic.

    The parser now gives more weight to explicit owners when resolving possessives. If a noun phrase is qualified by a possessive ("my book"), and the noun phrase is ambiguous even with the possessive qualifier (because the possessor is carrying more than one matching object), the parser will now choose an object with an explicit "owner" property over one without an explicit owner. When the parser resolves this kind of ambiguity by choosing an explicitly owned object over one that's merely being carried, it will mark the chosen object with the UnclearDisambig flag to indicate that it's a best guess.
    The library no longer treats "brightness" (the sensory intensity of light, sound, etc.) as diminishing over a "distant" containment boundary. In the past, distance diminished brightness by one level. While this was arguably physically realistic over large distances, in practice it proved to be undesirable for the typical scales in IF settings, where "distant" is usually used to indicate that something's tens or hundreds of feet away, not miles.
    The sense mechanism has a new feature that allows an object to take on its own special sensory status. An object can use this feature to override the normal sense path mechanism and decide for itself how it appears to the senses. The change is comprised of two new methods.

    First, when building the sense information table, the library now calls a new method, addToSenseInfoTable, on each object connected by containment to the source object. This method by default does what the library has always done: it evaluates the temporary sense properties set up by the containment path traversal, and adds a SenseInfo object to the table based on the sense properties. An objects can now override this method to add a table entry based on a different sensory status.

    Second, when finding the containment paths from one object to another, the library calls the new method specialPathFrom on the target object if (and only if) no ordinary containment path can be found. This method can supply its own custom containment path. This allows an object to exist outside of the normal containment hierarchy but still get a chance to represent its connection to the normal containment hierarchy as it sees fit.

    When a CollectiveGroup object has no location, it now takes on the sensory status of its individuals. It will take on the "best" sense status of any individual in scope, which is the one that's most transparent and (transparencies being equal) has the highest ambient level.

    This change makes it much easier to work with CollectiveGroup objects, because it makes a CollectiveGroup as visible, audible, etc. as any of its individuals.

    Note that this change doesn't affect CollectiveGroup objects that have a normal location. These objects already participated in the sensory model in the normal manner, and will continue to do so. This change only affects group objects with no location.

    The handling of the "catch-all" Default handlers has changed slightly. The sequence of processing is now as follows:

    In the past, the 'verify', 'check', and 'action' methods for the Default handlers were called in addition to any verb-specific versions of the methods. For example, if an object defined a dobjFor(Default) with an action() method, and the player entered an OPEN command on the object, the Default action handler ran, and then the dobjFor(Open) action() method also ran. In most cases, this made no difference, because the base Thing class doesn't even define action() or check() handlers for most verbs, since it disallows most verbs in the verify() stage.

    With this change, the Default handlers now run instead of the verb-specific handlers, when the Default handlers run at all. The rules about when Default handlers override verb-specific handlers, and vice versa, haven't changed. A Default handler still overrides any verb-specific handler inherited from a base class, and any verb-specific handler defined in the same class as the Default handler or in any subclass still overrides that Default handler.

    Note that the relative order of the All and Default handlers has been reversed: the All handler now always runs first. This makes the sequence of methods proceed from most general to most specific.

    If an 'exit' occurs within a PreCondition object's checkPreCondition() method, it's now treated as a failure of the enclosing action that invoked the precondition. The normal pattern that most preconditions use is as follows: first, evaluate if the condition is met; second, if the condition isn't met, try an appropriate implied action to try to bring it into effect; third, re-test the condition to see if the implied action did what it was meant to; and fourth, if the condition still doesn't apply, use 'exit' to terminate the triggering action. It's this use of 'exit' that's now interpreted as a failure of the enclosing action.

    Note that this doesn't apply if 'exit' is used within a nested implied action; this only applies if 'exit' is used within the precondition's checkPreCondition() method itself.

    The library now defines a template for the Achievement class, taking a double-quoted string for the achievment's description text.
    The new function finishGameMsg() makes it easier to show one of the conventional end-of-game messages, such as "*** YOU HAVE DIED ***" or "*** YOU HAVE WON ***". This function takes two parameters: a message specifier, and a list of extra finishing options. The extra options are the same as for the existing function finishGame().

    The message specifier can be one of several standard, pre-defined objects, or it can simply be a string to display. The pre-defined object ftDeath, ftVictory, ftFailure, and ftGameOver display messages for death of the player character ("YOU HAVE DIED"), victory ("YOU HAVE WON"), failure ("YOU HAVE FAILED"), and simply "game over", respectively. Alternatively, you can make up your own messages for unconventional cases ('YOU HAVE SUFFERED A FATE WORSE THAN DEATH', say) simply by specifying a string. Your string will be displayed surrounded by "***" sequences to yield the conventional formatting, or with other sequences that might vary by language. You can also pass nil as the message specifier, in which case no message at all is displayed.

    The value of gameMain.maxScore (formerly libScore.maxScore) is now allowed to be nil. In addition, the default value in GameMainDef is now nil. When this value is nil, the SCORE and FULL SCORE commands simply won't mention a maximum score for the game.

    If your game has complex scoring that makes it difficult or impossible to state a maximum possible score, or if you simply don't want to give this information to the player, simply set maxScore to nil in your gameMain object. The library uses nil as the default because there's no reason for the library to assume that a game will have a particular maximum score (the library formerly used a default value of 100, but this was overly presumptuous).

    The library's scoring system has a new, optional usage style that provides two benefits. First, it lets you define the number of points an Achievement is worth as part of the Achievement object, better encapsulating the information about the Achievement. Second, the library can use this new information to automatically compute the maximum possible score in the game, saving you the trouble of figuring this out manually (and of keeping the stated maximum in sync with the game as you make changes).

    If you explicitly set the maxScore property in your gameMain object, the library will not automatically compute the maximum score. Your explicit maxScore setting always overrides the computed value.

    To take advantage of the new capabilities, simply define the new 'points' property for each Achievement object you create. Then, rather than calling addToScore() or addToScoreOnce(), both of which take a parameter specifying the number of points to award, call the new Achievement methods awardPoints() or awardPointsOnce() instead. For example, suppose you have some existing code that looks like this:

      vase: Thing
        handleBreakage()
        {
          // ... do the real work here...
    
          scoreMarker.addToScoreOnce(10);
        }
    
        scoreMarker: Achievement { "breaking the base" }
      ;
    

    This code defines a nested object called 'scoreMarker' to describe the scoring item, and the 'handleBreakage' method awards the Achievement object, assigning it 10 points in the score. Using the new style, you'd change the code above to look like this instead:

      vase: Thing
        handleBreakage()
        {
          // ... do the real work here...
    
          scoreMarker.awardPoints();
        }
    
        scoreMarker: Achievement { +10 "breaking the base" }
      ;
    

    The "+10" in the nested Achievement definition assigns the object a value of 10 in its 'points' property (using a template defined in adv3.h), indicating that the item is worth 10 points in the score. Rather than specifying this in the code that awards the Achievement, we define it as part of the Achievement object itself. The code that awards the points doesn't need to specify the number of points; it just calls the awardPoints() method of the Achievement object, which awards the points defined in the object. This makes the code easier to read, since you can see the description of the scoring item and the number of points it's worth in one place.

    To take advantage of the library's automatic computation of the maximum possible score, you have to follow a few rules:

    If you follow these rules, the library will accurately compute the maximum score value, so you don't need to bother figuring out how many points are in the game yourself, and you don't need to worry about initializing libScore.maxScore in your game's start-up code.

    If your game can't follow the rules above (because you have alternative solutions to puzzles that assign different scores, for example, or because your game has alternative paths with different maximum scores), you'll need to figure the maximum score manually, and set the property 'maxScore' to this value in your 'gameMain' object. Explicitly setting gameMain.maxScore in your code will override the library's automatic computation.

    The finishGame() and finishGameMsg() functions now display the current score (using the same message that the SCORE command normally displays) if the finishOptionFullScore option is in the extra options list. In fact, these functions will announce the score if any extra option has the property showScoreInFinish set to true; finishOptionFullScore has this option set to true, so including it in the extra options list causes the score to be announced. If you want the end-of-game announcement to include the score, but you don't want to offer the FULL SCORE option, you can include finishOptionScore in the extra options list; this is an "unlisted" option, so it doesn't add anything to the list of offered option list, but it does cause the end-of-game announcement to include the score.
    Actor.holdingDesc (which provides the default description of what an actor is holding as part of processing an Examine action on the actor) now uses pretty much the same handling that Thing uses when it lists the contents of an object. The only difference is that Actor.holdingDesc still uses the special holdingDescInventoryLister as the contents lister. One important consequence of this change is that examining an actor marks the actor's contents as having been seen by the player character, just as examining an ordinary object marks the object's contents as seen. Another is that the scope of objects included in the listing is limited to objects the point-of-view actor can actually see. In the past, the examined actor listed everything in "inventory scope," which includes objects that are directly held even if they're not currently visible; this is correct for an inventory listing from the point of the view of the actor taking the inventory (because the actor can presumably identify directly held items by feel, even in the dark), but the new sight-only behavior is more appropriate for examining another actor.

    This change also adds a new Thing method examineListContentsWith(). This is simply a service method that performs the bulk of what Thing.examineListContents() did before, but is parameterized by a lister to use to generate the listing.

    The new class AskConnector makes it easy to define a directional connection when the direction is ambiguous. For example, suppose that you have two doors leading north, and you don't want to use the traditional trick of distinguishing the doors' respective directions as northeast and northwest. (If you had ten doors leading north instead of two, the traditional northeast/northwest trick wouldn't help anyway.) If the player types NORTH, you would like the parser to ask which door to use.

    AskConnector makes this easy. Simply define the room's 'north' as an AskConnector object, and set the AskConnector instance's property 'travelAction' to 'GoThroughAction'. The connector, when invoked for travel, will try running the GoThroughAction, but with a missing direct object; this will make the parser ask the player which door to use with the usual prompt for a missing object: "What do you want to go through?"

    If the direction has a specific set of possible objects, as in our example of two doors leading north, you can further refine the parser's question by specifying the 'travelObjs' property. Set this to a list of the possible direct objects for the action. If you set this property, you should also set 'travelObjsPhrase' to a string giving the noun phrase to use in disambiguous questions: in our example, we might set this to 'door', so that the question becomes "Which door do you mean...?".

    The English library now provides a default verbPhrase for the TravelVia action ('use/using (what)', which is pretty vague, but TravelVia is fairly vague itself). This lets the parser generate usable messages in cases where a TravelVia action elicits a parser prompt, such as when using TravelVia in askForDobj().
    TravelPushable has several small changes.

    First, TravelPushable.movePushable() now uses moveIntoForTravel() to move the object, rather than moveInto(). This corrects a problem that occurred when the starting and destination locations were connected by a sense connection (such as a distance connector).

    Second, movePushable() and describeMovePushable() each now take an additional argument giving the connector being traversed.

    Third, the new method beforeMovePushable() gives the pushable object a chance to do any necessary work just before the travel takes place. This is especially useful for adding a message describing anything special that happens when pushing the object out of its current location. By default, this routine does nothing.

    The new Traveler methods canTravelVia() and explainNoTravelVia() allow a traveler to disallow travel via a particular connector and/or to a particular destination. These new methods essentially provide a complement of the "travel barrier" mechanism, which allows a connector to disallow travel by a particular traveler. The new methods are useful becuase it's often more convenient to tie a travel condition to the traveler than to the connector. For example, you might want to implement a condition that prohibits a particular actor from going outside; you could do this by defining a canTravelVia() method on the actor that returns nil if the destination is an OutdoorRoom.
    The new BasicLocation methods enteringRoom(traveler) and leavingRoom(traveler) make it more convenient to write code that responds to an actor's arrival into or departure from a room. These methods are called from travelerArriving() and travelerLeaving(), respectively; they differ only in that (1) they have simpler parameter lists, leaving out information that frequently isn't needed to write an arrival/departure event handler, and (2) the base class implementations don't do anything, so there's no need to use 'inherited' to inherit up the base class behavior. These new methods are purely for convenience, to make the very common task of writing arrival/departure event handlers a little less work.
    The new class ContainerDoor makes it easy to create a door for a container, if you want the door to act like a separate object in its own right. This works with the ComplexContainer class: create a parent as a ComplexContainer, and inside it create a ContainerDoor and the normal secret actual Container. The door will redirect commands like open, close, lock, and unlock to the secret inner container, but will still appear as a separate object.
    In the past, the base TravelConnector methods describeArrival() and describeDeparture() simply showed the most generic travel messages ("Bob is leaving the area"). This meant that replacing a simple room connection with a TravelMessage connector meant giving up the directional message that a normal room connection generated ("Bob is leaving to the east").

    Now, the directional message is the default in the base TravelConnector class. If there's a direction property linked to the connector from the origin (for a departure) or destination (for an arrival), the base TravelConnector messages will now show the directional version of the message. The generic, non-directional message will only be shown when no directional link can be found. Note that this doesn't affect the more specific types of connectors and their custom messages, so stairs will still say "Bob goes up the stairs," doors will still say "Bob leaves through the wooden door," and so on.

    The new TravelConnector method isConnectorPassable() indicates whether or not a traveler can pass through the connector. This can be used in game code that probes the map to determine if a connector can be traversed in its current state.
    In the past, the noTravel object, and the NoTravelMessage and FakeConnector classes, incorrectly displayed their special messages on attempted travel even when it was dark in the room. In the dark, the "dark travel" message should take precedence. This has been corrected. (The problem was that these classes overrode the dobjFor(TravelVia) check() method to bypass the normal darkness check. The unnecessary override has been removed, so the standard darkness check is now made, so the dark-travel message is now shown instead of any special message.)
    The BasicLocation methods getTraveler() and getPushTraveler() have been renamed and moved into Thing. These methods are now called getLocTraveler() ("get location traveler") and getLocPushTraveler() ("get location push traveler"), respectively.

    This change is necessary to allow actors to hold other actors, and to allow intermediate containers within rooms (that is, to allow a Room to hold an arbitrary Thing subclass, which in turn holds a NestedRoom). Actor has methods with the names getTraveler() and getPushTraveler(), but these methods have a different purpose than the old BasicLocation methods: the Actor methods get the traveler when the actor is initiating the travel, and the old BasicLocation methods get the actual traveler when a traveler within the location initiates travel. The use of the same name created a conflict between these different purposes, so one or the other set had to be renamed. In addition, limiting these methods to BasicLocation prevented ordinary Things from holding actors who could travel; adding them to Thing corrects this.

    The logic in Vehicle.getTraveler() that determines whether to move the vehicle or the traveler within the vehicle has changed slightly. Rather than basing the decision on travel barriers, the routine now moves the traveler rather than the vehicle only if the connector is contained within the vehicle - that is, the connector leads somewhere from inside the vehicle. The connector is considered to be inside the vehicle if (1) it's a physical object (a Thing) that's inside the vehicle, or (2) one of the direction properties of the vehicle is set to the connector. (The old logic had the undesirable side effect of implicitly removing the traveler from the vehicle if the vehicle couldn't cross a barrier, which was really too automatic.)
    In the past, the way push-travel actions was handled caused a problem in certain cases involving remapping of actions. The problem showed up most readily with StairwayUp and StairwayDown objects; specifically, PUSH x DOWN acted differently than PUSH x DOWN y, where y was a StairwayUp or StairwayDown and the room's 'up' or 'down' pointed to y. These commands obviously should have been equivalent.

    Stairways were the only library objects that exposed the problem, but it was possible to define your own objects with the same bad behavior. In general, the problem occurred whenever a two-object push-travel action (PUSH x UP y, PUSH x THROUGH y, PUSH x INTO y, etc.) was attempted, and the indirect object's handler for the corresponding travel action (ClimbUp, GoThrough, Enter, etc.) was defined using asDobjFor(), and the handler for the target action of the asDobjFor() was itself defined using a remapTo().

    The problem has been fixed, so any two-object push-travel action should now be handled equivalently to the corresponding one-object (directional) push-travel action on the same travel connector.

    The Chair, Bed, and Platform classes can now all allow standing, sitting, and lying on the object. In the past, Chair only allowed sitting, Bed allowed lying and sitting, and Platform allowed all three. The set of postures allowed can now be customized for each individual object.

    The new property allowedPostures contains a list of the postures that the object allows. By default, Chair allows sitting and standing, and Bed and Platform allow sitting, standing, and lying. You can override allowedPostures for an individual Chair, Bed, or Platform object to add or remove allowed postures. When a posture isn't in the allowed list, it will be ruled as "illogical" in the verification stage.

    In addition, the new property obviousPostures contains a list of the postures that are "obvious" for the object; these are the postures that are most natural for the object. The obvious postures are enumerated separately from the allowed postures to control defaulting: if a posture is allowed but not obvious for an object, then the corresponding command will be ruled as "nonObvious" in the verify stage, ensuring that the command will only be accepted if stated explicitly (that is, the player will be able to perform the command, but the parser won't automatically guess the command based on incomplete information from the player). By default, it's obvious to sit on a chair, to sit or lie on a bed, and to sit, lie, or stand on a platform.

    The three separate classes are still three separate classes for a couple of reasons. First, each one has a primary, "natural" posture associated with it: sitting on a chair, lying on a bed, standing on a platform. This primary posture is the one that the library will assume for implied actions and incomplete commands. Second, Platform differs from the other two in that objects dropped while standing on a platform land on the platform, whereas objects dropped while on a chair or bed land in the enclosing room.

    Note that a posture that isn't allowed by allowedPostures will be treated as illogical. If you want to create an object for which a given posture makes physical sense, but isn't allowed for some other reason (character motivation constraints, for example), you should include the posture in allowedPostures and then disallow the corresponding command using the check() routine.

    NestedRoom now defines 'out' to use the special 'noTravelOut' connector, which will automatically treat an OUT command as a GET OUT OF command while in the nested room. Without this, the 'noTravelOut' would typically be "inherited" from the enclosing room anyway, but not always: if the enclosing room defined its own OUT, the nested room would pick up that one instead, which isn't usually appropriate when within a nested room. This change ensures that nested rooms behave consistently even when nested within rooms with their own explicit 'out' connections.
    The HighNestedRoom class now prohibits exiting the room, by default. It does this by setting the exitDestination property to nil; this indicates that the room has no natural exit location, so it's not possible to leave it. HighNestedRoom generates an appropriate message ("It's too long a drop to do that from here") when an attempt to exit the location fails.

    The base NestedRoom now checks, in the GetOutOf action's check() handler, that there is a non-nil exitDestination. If there's not a valid exit destination, the check() handler calls the new method cannotMoveActorOutOf() to display an appropriate message, then terminates the command with 'exit'.

    By default, NestedRoom now uses the new travel connector nestedRoomOut for its "out" link, rather than noTravelOut as it did in the past. The nestedRoomOut connector works just like noTravelOut, except that nestedRoomOut is "apparent" to characters, so it shows up in the EXITS list. Since OUT is usually a valid travel command while in a nested room (the command exits the nested room), it makes sense to list OUT explicitly as a possible direction while in a nested room. To override this for a particular nested room, just set 'out' to noTravelOut; to change this behavior for all nested rooms, do the same thing with 'modify NestedRoom'.
    The new Floorless class is a mix-in that can be combined with any other type of Room class to create a floorless version of the room. Mix Floorless in ahead of the Room base class in the superclass list of the room you're creating; Floorless will, among other things, subtract any default floor/ground object from the base room's roomParts list.

    The existing FloorlessRoom class has been retained, but is now simply a convenience class that combines Floorless and Room. This provides the same behavior as the old FloorlessRoom class, so existing code should not be affected.

    Some fine-tuning has been applied to announcements for cascading implied actions in certain cases. When a series of nested implied actions fails, the parser now announces only the first of the series. For example, in the past, if a travel action initiated an OPEN DOOR which initiated an UNLOCK DOOR which failed, the parser showed something like this: (first trying to unlock the door and then open it). The parser now shows only this: (first trying to unlock the door).

    The reason for dropping the extra announcements is that they don't really tell the player anything useful, so they're just unnecessary verbosity. Because of the recursive relationship of the implied action in these cases, the first action is nested in the second, which is nested in the third, and so on; so the second and subsequent actions all failed as a direct result of the first one failing (in our example, the OPEN fails because it requires the UNLOCK to succeed, so when the UNLOCK fails the OPEN must also fail). This means that we never actually get around to trying the second and subsequent actions; when the first action fails, we give up on trying any of the others. The only thing they tell the player is why the failed action was attempted at all (in our example, the implied OPEN explains why we're attempting the implied UNLOCK). But the rationale in these cases is almost always obvious to the player, so even this isn't a very good reason to keep the extra announcements.

    This behavior can be controlled via implicitAnnouncementGrouper.keepAllFailures. The property is nil by default; to list the entire stack of failures for these cases, change it to true.

    The extra "testing" step for opening Lockable objects has been removed. This extra step was introduced in 3.0.6m to make the cascade of implied actions more plausible for these cases, but the extra verbosity was really too much, so it's been removed in the interest of simplicity.

    Instead, a Lockable now tests a new method, autoUnlockOnOpen(); if this returns true, then OPEN implies UNLOCK, otherwise it does not. This means that, if autoUnlockOnOpen returns nil, and the object is locked, then an OPEN command will simply fail with an error message ("the door seems to be locked"), and the player will have to enter an explicit UNLOCK command.

    The new autoUnlockOnOpen() method is defined as follows:

    Some authors might always prefer the automatic UNLOCK to forcing a player to type a separate UNLOCK command for objects with non-obvious locking status. Which way you prefer is a matter of taste. On the one hand, the extra UNLOCK command is a little annoying to players, and is exactly the sort of thing the precondition mechanism was created to avoid. On the other hand, the automatic UNLOCK is weird when the lock status isn't apparent, because until the OPEN fails, we have no way of knowing that the object was locked in the first place and thus no reason to try the implied UNLOCK; it's an instance of the parser revealing game-state information that the player character isn't supposed to know. It also could be seen as actually detracting from playability by making the game do too much automatically, taking away a certain amount of control from the player (the player could think: "I didn't even know that door was locked; if I had, I wouldn't have tried to unlock it right now.")

    By default, the library implements the more realistic but also more tedious behavior, requiring a separate UNLOCK to OPEN a locked object whose lock status isn't obvious. If you prefer to make UNLOCK automatic on OPEN for a given object, simply override autoOpenOnUnlock() to return true unconditionally for that object; if you prefer to make UNLOCK automatic for all objects, modify Lockable to make autoOpenOnUnlock() return true in all cases.

    The implied command behavior of keyed-lockable objects has been changed slightly. In the past, attempting to OPEN a keyed-lockable triggered an implied UNLOCK, which asked for a key. Thus, a command like OPEN DOOR, or even GO NORTH, could be answered with a question ("What do you want to unlock it with?"). This was a little awkward because the player never actually said in so many words that they wanted to unlock the door. Now, keyed-lockable objects simply point out that a key is required to open the lock:

      >go north
      (first trying to unlock the iron door)
      You need a key to unlock the door.
    

    Note, however, that if the actor knows which key to use, and one of the known keys is in sight, then the command will be allowed to proceed implicitly. In these cases, it won't be necessary to ask for the key, since the actor's knowledge of the key allows it to be supplied automatically as a default.

    The Keyring class now follows the normal Thing conventions for mentioning the contents of the keyring. Formerly, Keyring.desc generated the list of contents, using a lister defined in the property 'examineLister'. Now, Keyring has no separate 'desc' method; instead, the listing is generated by the standard Examine code inherited from Thing, and the 'examineLister' property has been renamed to 'descContentsLister' to accommodate the inherited Thing methods.
    The new class CustomImmovable makes it easy to create an Immovable that uses a single customized message to respond to all attempts to move the object. Just override cannotTakeMsg, and the "move" and "put in" messages will use the same message. The new class CustomFixture does the same thing for Fixture.
    The new class FueledLightSource abstracts some of the functionality that was formerly in Candle, to create a base class for objects that provide light using a limited fuel supply. The new base class doesn't assume that it's something that burns, as Candle does, so you can use the new class to create things like limited-use flashlights. Candle is now based on the new class. This change shouldn't affect any existing game code; the implementation of Candle has merely been rearranged internally to the library, so no changes should be required to existing code.

    An additional improvement in the new FueledLightSource class is that the fuel source is no longer assumed to be the object itself. This lets you model the fuel source as a separate object; for example, you could use a separate battery object as the fuel source for a flashlight, allowing a character in the game to swap out a dead battery for a new one. Simply override the 'fuelSource' property of your FueledLightSource object to specify the object that provides the fuel. By default, the fuel source is still 'self', so Candle objects in existing source code won't need any changes.

    The tryMakingRoomToHold method in Actor now checks each object it's about to move into a "bag of holding" to make sure the object that's to be held isn't inside the object to be moved. This ensures that the attempt to make more room to hold things doesn't inadvertantly move the object to be held out of reach, defeating the purpose of making room to hold it. This change ensures that the object to be held stays where it is, since all of its containers stay where they are.
    UnlistedProxyConnector.ofKind now returns true if the caller is asking about UnlistedProxyConnector, and also returns true if the caller is asking about the class of the underlying connector. This makes a proxy look like its underlying connector and also like a proxy, which is consistent with the rest of its behavior.
    The new DefaultTopic property 'excludeMatch' can be set to a list of topics (Thing or Topic objects) to exclude from the catch-all match. By default, this is just an empty list, so nothing is excluded. Individual DefaultTopic objects can provide a list of objects to exclude from the catch-all. This is useful when you're creating a DefaultTopic for a ConvNode or ActorState, but you want one or more specific topics to be referred to the enclosing topic database for handling. By including the topics in the excludeMatch list, you ensure that they won't be caught in the catch-all DefaultTopic, which will let them propagate to the enclosing topic database.
    ConvNode.npcContinueConversation() now returns true if anything was displayed in the course of the method, nil if not. This lets a caller determine if the NPC actually had anything to say.

    ActorState.takeTurn() now uses this information to consider the NPC to have finished its turn if the ConvNode added a conversational message. In the past, merely having a non-nil ConvNode was enough to make takeTurn() consider the turn to be taken entirely by the ConvNode; now, the turn is only considered finished if the ConvNode actually wanted to say something. This allows ordinary actor-state background scripts to continue running even when a ConvNode is active, as long as the ConvNode doesn't generate any NPC conversation messages.

    The nestedActorAction() function now sets a visual sense context for the new actor. (In the past, the nested action incorrectly executed within the same sense context as the enclosing action, which allowed actions that shouldn't have been visible to be reported, and vice versa.)
    If an implied action announcement comes immediately after other report messages for an action in which the implied action is nested, the command transcript now automatically inserts a paragraph start just before the implied action announcement. It's often desirable to invoke a nested action in the course of handling a main action, and in some cases the nested action can trigger implied actions of its own. This new transcript feature makes the spacing look better in these cases when the main action reports its own results before invoking the nested action. Similarly, if an implied action announcement occurs after a result message from a previous implied action, a paragraph break is inserted before the second implied announcement. This improves readability in these cases.
    The CommandTranscript method summarizeAction() now interacts properly with conversational responses. A change in 3.0.6m caused a problem when attempting to summarize a series of conversational messages (generated by TopicEntry activation); specifically, the summarizer didn't recognize the new "in-band" conversational boundary markers that the conversation manager started generating in 3.0.6m. The summarizer now handles the conversation markers properly.
    The AgendaItem class has a new property, initiallyActive, that indicates that an agenda item is active at the start of the game. During initialization, the library will automatically add each initially active agenda item to its actor's agenda (by calling addToAgenda() on the actor).
    The new AgendaItem property agendaOrder makes it easy to control the priority order of agenda items. The agenda list is now sorted in ascending order of the agendaOrder values; this means that the lower the number, the earlier the item will appear in the agenda list. Actors choose the first item in the list that's ready to run, so ready-to-run agenda items will always be executed in order of their agendaOrder values. By default, agendaOrder is set to 100; set a lower value if you want the item to go earlier than other items, a higher number if you want it to go later. If you don't care about the ordering, you can leave the default value unchanged.

    Note that readiness is more important than agendaOrder. Suppose that AgendaItem A has a lower agendaItem value, and thus goes earlier in the list, than AgendaItem B. Despite this list order, if B is ready to run and A is not, B will be executed.

    The new class SuggestedAskForTopic can be mixed into an AskForTopic object's superclass list to add the "ask for" to the topic inventory. This works in parallel with the other SuggestedTopic subclasses.
    In the past, AltTopic didn't work properly with SpecialTopic. This has been corrected; you can now create AltTopic children of SpecialTopic entries, and they'll work as you'd expect.

    In a related change, SpecialTopic now inherits from SuggestedTopicTree, rather than SuggestedTopic as it did in the past. This ensures that a special topic will be suggested in the topic inventory when any of its AltTopic children are active.

    The library now defines template variations that allow defining firstEvents lists with various TopicEntry/ShuffledTextList combinations: TopicEntry, MiscTopic, SpecialTopic, DefaultTopic, AltTopic. Now, if you define an instance of one of these classes, your definition can include two lists: the first will be the firstEvents list, and the second will be the eventList list.
    The new DefaultAskForTopic class lets you define a default response to an ASK FOR command. This new class is parallel to the other DefaultTopic subclasses.
    The various DefaultTopic subclasses are now arranged in a hierarchy of low "match score" values. In the past, all DefaultTopics used a match score of 1, which generally made them rank lower than any non-default topic, but didn't differentiate among different default topics that matched the same input topic. Now, the single-type defaults (DefaultAskTopic, DefaultTellTopic, etc.) have a match score of 3, the multi-type defaults (DefaultAskTellTopic, etc.) have a match score of 2, and DefaultAnyTopic has a score of 1. This means that a single-type default will take precedence over a multi-type default, which will in turn take precedence over an ANY default. This lets you define a DefaultAskTopic that will handle any ASK command, for example, and a DefaultAnyTopic to handle everything else.
    The initiateTopic() method (in Actor, ActorState, and ConvNode) now remembers that the player character is talking to the NPC. This ensures that commands that check the current interlocutor, such as TOPICS, properly recognize that the characters are talking to one another.
    A problem in the library generated a spurious "you're not talking to anyone" message under certain obscure circumstances involving an automatic "topic inventory" scheduled at the start of a conversation. This is now fixed.
    In some cases, if an actor was in a ConvNode but didn't say anything on a given turn, the actor was incorrectly set as the most recent pronoun. The library was being overly aggressive about setting the pronoun as though the actor had said something, as a convenience to the player to respond to the actor. The library no longer sets the pronoun unless the actor actually says something (i.e., some text is generated in the course of the actor's turn while in the ConvNode).
    In the English language module, the vocabulary initializer now ignores extra spaces after delimiters (such as '/' or '*'). This makes it easier to break up long vocabulary initializers across multiple lines of code. For example:

      + Thing 'big large huge gigantic giant book/tome/volume/
              text/manuscript' 'huge tome'
        // etc
      ;
    

    Note the line break in the middle of the list of nouns. This introduces an extra space into the string, which is now perfectly okay.

    In the English parser, the literal adjective wildcard string has been changed. In the past, "*" (an asterisk within double quotes) was the wildcard character. This made it difficult to match literally an asterisk in the input, so the wildcard is now the character \u0001 (i.e., Unicode code point 1), which is highly unlikely to be used in input.
    In the English parser, 'miscVocab' was used as a token property instead of 'miscWord' in a few places. The correct token type should have been 'miscWord'; this has been corrected.
    The menu system now "wraps" up-arrow and down-arrow keys at the top and bottom of a list of menu items; that is, if the first menu item is selected and user presses the up-arrow, the selection moves to the last menu item, and a down-arrow at the last item selects the first item.
    The library now provides a template for MenuTopicItem, with slots for the title string, an optional heading string, and the menu contents list.
    When a locked door with lockStatusObvious was examined, a message indicating its status (locked or unlocked) was meant to be displayed, but wasn't. This has been corrected.
    A recent change to OOPS processing (in 3.0.6j) introduced a bug: typing OOPS after a misspelling in a response to an interactive parser query didn't work. For example, if your answer to a disambiguation question ("which book do you mean...") contained a typo, you couldn't use OOPS to correct that typo. This now works properly again.
    Typing AGAIN after a SpecialTopic command didn't work in the past. This has been corrected. AGAIN can now be used to repeat a special topic, as long as the same comamnd is still valid as a special topic; if the command isn't a valid special topic any longer, the parser will simply say that the command cannot be repeated.
    The new Thing method expandPronounList() allows an object to effectively reverse the effects of filterResolveList() when a pronoun refers to the object in a subsequent command. The parser calls this new method on each of the objects in the "raw" binding for a pronoun (that is, the set of program objects that were resolved in the previous command that established the antecedent of the pronoun).

    The Thing and CollectiveGroup objects use this new method to ensure that a collective group's filtering is applied consistently for each command, whether the command uses a full noun phrase or a pronoun to refer to the groupable objects.

    When a noun phrase in a player command refers to an object that has "facets" (as indicated by the object's getFacets() method), and more than one facet of the object is in scope, the parser will now automatically choose only one facet of the object. The parser chooses the facet that has the best transparency in the actor's sight-like senses; if the visibilities are equivalent, then the parser chooses the facet with the best touch transparency; if visibilities and touchabilities are equivalent, the parser chooses one arbitrarily. This change ensures that different facets of the same object will never create ambiguity in the parser, which is important because a set of facets is always meant to implement what the player sees as a single game world object.

    In a related change, the parser's method of choosing a facet as the antecedent of a pronoun has changed slightly. First, the parser now considers all in-scope facets when an antecedent has facets, even if the base antecedent object is itself in scope. Second, the parser chooses which in-scope facet to use based on the same criteria as above: visibility, then touchability. This ensures that when multiple facets are in scope (because of sense connectors, for example), the most readily visible and reachable one will be selected.

    The parser now selects a default antecedant for a pronoun whenever possible, based on the objects in scope. For example, if you type LOOK AT HIM, and you haven't established a prior meaning for HIM (by referring to a male actor with a previous command), or the prior meaning of HIM is no longer valid (because that actor is no longer present), then the parser will assume that HIM refers to a male actor who is present, assuming there's exactly one. If there are no in-scope objects matching the gender of the pronoun, or multiple matching objects are in scope, the parser has no way of guessing what the pronoun means. When a pronoun could only refer to one object, though, the parser will assume that that's the object you meant.
    In 3.0.6l, the parser started accepting HIM and HER to select a unique gendered object from a disambiguation query ("which one do you mean..."). This handling has been improved slightly: the parser now treats HIM and HER as narrowing the choices, rather than requiring a unique match. If more than one of the choices matches the pronoun, the parser narrows the choices to the ones that match and asks for disambiguation help again, this time offering the reduced list of choices.
    A bug in turn timing showed up when an implied command used replaceAction(); due to the bug, such a command didn't consume a turn. This showed up, for example, when a travel command (GO NORTH) triggered an OPEN DOOR command, which in turn triggered an UNLOCK DOOR command, which replaced itself via askForIobj() with an UNLOCK DOOR WITH WHAT? command. This has now been corrected.
    In the various places where nested actions are created (nestedAction, nestedActorAction, remapTo, etc), it's now easier to specify literal and topic objects. In the past, it was necessary for the caller to wrap these types of objects in the appropriate internal parser structures, such as a ResolvedTopic. This is no longer necessary: actions that use special wrappers internally will now automatically create the appropriate wrappers for you.

    In practice, this means that you can simply use a string in any nested action context where a literal is required, and you can use a simple Thing or Topic object in any context where a topic is required. Examples that formerly required special coding, but now work in the obvious fashion:

    A remapTo() object argument can now be a string, when the new verb accepts a literal in that position. For example, you can use remapTo(TypeLiteralOn, typewriter, 'hello') to remap to the command TYPE "HELLO" ON TYPEWRITER.
    The parser failed to announce a defaulted object under certain obscure circumstances. In particular, if a remapTo() on a two-object action remapped a command, and the source of the remapping was itself chosen as a defaulted object, the announcement was missing. The announcement will now be generated.
    In libMessages, obscuredReadDesc() and dimReadDesc() incorrectly returned a string rather than displaying a message, resulting in no message being displayed when reading a Readable under dim or obscured visual conditions. This has been corrected; these libMessages methods now display their messages directly.
    In the default English messages, the various messages indicating that an actor is in the room and situated in or on a nested room (such as sitting in a chair, or standing on a platform) have been changed slightly. In the past, these said something like "Bob is here, sitting on the chair." Now, the essentially pointless "here" part has been dropped, so the messages read more simply: "Bob is sitting on the chair."
    The English library message for attempting to take an UntakeableActor referred to the wrong object (the actor instead of the direct object) in one of its clauses. The message has been corrected.
    The English library message listing the newly revealed objects after opening an Openable now uses specialized phrasing when an NPC performs the action: "Bob opens the box, revealing..." The PC version hasn't changed: "Opening the box reveals..."
    The English library's default message for attempting to ask oneself for something (ASK ME FOR...) incorrectly referred to the substitution parameter "{iobj}" (which isn't available with an ASK FOR command, because the second noun phrase is actually a topic phrase). This has been corrected.
    The English version of the INSTRUCTIONS command now automatically senses the presence of certain optional conversation features and mentions the corresponding commands only if they're used in the game. In particular, the TOPICS command will be mentioned only if there are SuggestedTopic instances in the game; the existence of special topics will be mentioned on if there are SpecialTopic instances; and the TALK TO command will be mentioned only if there are InConversationState instances. This reduces the manual work needed to customize the conversation system instructions; if your game uses these features, they'll be mentioned, otherwise they won't.
    A style tag name can now contain digits, as long it starts with an alphabetic character. (In the past, style tag names were only allowed to contain alphabetic characters. This makes style tag naming more consistent with the HTML tag naming rules.)
    The new function overrides(obj, base, prop) makes it easy to test whether or not the given object 'obj' overrides the property 'prop' that 'obj' inherits from base class 'base'. This function returns true if 'obj' derives from 'base' (that is, obj.ofKind(base) returns true), and 'obj' and 'base' get their definitions of 'prop' from different places.

    In the past, the library tested in a few places to see if a given property or method was inherited from a given library base class. This was almost the same test that overrides() performs, but didn't work properly in cases where a library base class had been modified by a game or library extension; when 'modify' is used to extend a class, the defining class for methods defined in the original (pre-modification) version of the class is the original version, not the new version created by 'modify', which takes on the original name. To allow for 'modify' usage, all of the tests the library used to make along these lines have now been replaced with calls to overrides() instead.

    The SENSE_CACHE conditional compilation has been removed, so the sense caching code is now permanently enabled. This shouldn't affect any existing code, since I'm not aware of anyone who's wanted to use the option to disable the sense cache.

    (This is simply an internal change to eliminate some unnecessary extra source code that's been disabled for a long time anyway. When the sense-cache mechanism was first introduced, it was made optional, so that any games that had problems with the caching could turn it off. The code has been active by default for a long time now, and I'm not aware of anyone who has found it necessary or desirable to disable it, so there no longer seems to be any value in the added complexity of having both options. Removing the conditional option simplifies the source code by removing the effectively dead code for the option of disabling the sense cache.)

    Due to a bug in the parser, a run-time error occurred in certain cases when the player entered a two-object command that was missing its indirect object, and then answered the parser's prompt for the missing object. This has been corrected.
    The standard library tokenizer now accepts function pointers and anonymous function pointers in the fourth slot (the converter callback) in a token rule. The function is invoked using the same arguments that are used when calling a method of 'self' to convert the token value.
    3.0.6m

    Released November 15, 2003

    Important compatibility-breaking change #1: The 'explicit' parameter has been removed from Thing.lookAround() and Actor.lookAround(), as well as from the internal Thing service methods lookAroundPov(), lookAroundWithin(), and lookAroundWithinSense(). This extra parameter was an historical relic that's no longer used, so it's being removed to simplify the interfaces to these routines. Game code is likely to call lookAround() in a few places, and might even override it, so authors should check their existing game code and remove this parameter from any calls or overrides.
    Important compatibility-breaking change #2: The AltTopic mechanism has been changed slightly. In the past, AltTopics were nested one within another to form a group of alternatives. Now, AltTopics are still nested within their parent TopicEntry, but they're no longer nested within one another; instead, they're simply siblings. So, for old game code like this:

      // OLD WAY!!!
      + TopicEntry @lighthouse "It's very tall...";
      ++ AltTopic "Not really..." isActive=(...);
      +++ AltTopic "Well, maybe..." isActive=(...);
      ++++ AltTopic "Okay, it is..." isActive=(...);
    

    ...you'd now instead write it like this:

      // the new way
      + TopicEntry @lighthouse "It's very tall...";
      ++ AltTopic "Not really..." isActive=(...);
      ++ AltTopic "Well, maybe..." isActive=(...);
      ++ AltTopic "Okay, it is..." isActive=(...);
    

    The only change you need to make is to remove the additional nesting from the second and subsequent AltTopic in each group - simply put all of the AltTopics in a group at the same '+' nesting level.

    The old nesting scheme had a tendency to get unwieldy whenever a single topic had more than one or two alternatives. The new scheme makes game code a little cleaner when defining large AltTopic lists.

    Possible compatibility-breaking change #3: The getTraveler() methods, in both Actor and BasicLocation, now have an additional parameter giving the connector being traversed. This allows the routine to take into account both the actor and the connector involved in the travel, which can be useful in some cases.

    For example,