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.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, you might want to set up a travel connector that leads out of a vehicle, in which case you'd want the actor within the vehicle rather than the vehicle to be the traveler when the connector is traversed. In these cases, you'd override the vehicle's getTraveler() method to check the connector, returning the actor rather than the vehicle when the outbound connector is the one being traversed.

In addition, for greater generality, the "actor" argument to BasicLocation.getTraveler() (and subclass overrides) has been renamed to indicate that it now takes a traveler rather than merely an actor. Vehicle.getTraveler() takes advantage of this: it now recursively asks its container for the traveler, passing itself (rather than the traveler within itself) as the proposed traveler. These changes allows for situations where an actor is inside a vehicle that's inside another vehicle, for example.

Finally, Vehicle.getTraveler() now makes an additional check before returning itself as the traveler: if the connector allows the traveler within the vehicle to pass (as indicated by the connector's canTravelerPass() method), but the connector doesn't allow the vehicle itself to pass, then getTraveler() simply returns the original traveler rather than trying to make the vehicle travel. This makes it easy to set up an outbound connector from within a vehicle - simply set up the connector's canTravelerPass() to exclude the vehicle itself, and a traveler within the vehicle will automatically leave the vehicle behind on traversing the connector.

There are a few minor interface changes in the travel mechanism, all of which should be transparent to existing game code.

The TravelConnector method connectorBack() now takes a Traveler object rather than an Actor as its first argument.

The TravelConnector method fixedSource() now takes a Traveler instead of an Actor as its second argument.

The Thing method getTravelConnector() now accepts a nil 'actor' argument. When the actor is nil, it means that the caller is interested in the structure of the map independently of any actor's present ability to perceive that structure. This is useful in some cases, such as auto-mapping, where we want to know what the map is actually like rather than merely what it looks like to a given actor at a given time.

The internal handling of the various PushTravel action subclasses now uses only the 'verify' stage of the action handling for the indirect object, when there's an indirect object at all. For example, PUSH BOX THROUGH DOOR only calls the door's dobjFor(GoThrough) 'verify' handler, not any of its other handlers. This change reflects the fact that the action of a push-travel is always carried out by a nested TravelVia with the special PushTraveler object, hence the only need we have for the indirect object handlers at all during the initial PushTravel handling is for disambiguation via the 'verify' routine. The old implementation was problematic when the indirect object remapped decomposed verb, because it incorrectly remapped the entire action before the proper nested action could be invoked. This change corrects the problem, allowing the PushTravel varieties to work correctly for any combination of objects.

In the Traveler class, describeDeparture() and describeArrival() now show their messages in a visual sense context for the traveler, if the player character isn't involved in the travel. This ensures that the travel is properly described (or properly not described) in situations where the traveler is visible to the player character but the motivating NPC isn't, or vice versa, such as when an NPC is inside an opaque vehicle.

The Fixture and Immovable classes are now based on a new common base class, NonPortable. This change is entirely a matter of the library's internal organization, and is entirely transparent to game code - Fixture and Immovable behave exactly like they did before.

The new class was introduced for two reasons. First, it consolidates the behavior common to all non-portable objects, eliminating a small amount of redundancy in the former arrangement. Second, and much more importantly, it roots all of library's non-portable object classes in a single base class. This means that code can reliably test an object for unportability by checking obj.ofKind(NonPortable). In the past, it was necessary to write (obj.ofKind(Immovable) || obj.ofKind(Fixture)); not only is that test more cumbersome, but its very form is suggestive of the potential for additional "||" clauses in the future. The new arrangement formalizes in the library a single root class for all unportable objects, ensuring a clean way for the library and library extensions to add any future specialized unportables without breaking existing game code.

Note that this change does not break existing game code that uses the "||" test; other than the new common base class, the inheritance structure for Fixture and Immovable and their subclasses has not changed. Even so, it wouldn't be a bad idea to replace any "||" tests in your existing code with the simpler ofKind(NonPortable) test, because this will ensure that your code takes advantage of the insulation from future changes that the new class affords.

The Fixture class (and by inheritance, its subclass Component) now considers an instance to be owned by a given object if the object is the Fixture's location, or the location is owned by the object. Since a Fixture/Component is a permanent part of its location, it usually is appropriate to consider the component to be owned by anything that owns the location, as well as to consider the component owned by the location itself.
The new mix-in class Attachable (in extras.t) makes it easier to define objects that can be attached to one another, such as a hose and a faucet, a plug and an electrical outlet, or a rope and a railing. Attachable is meant to be combined (using multiple inheritance) with Thing or a Thing subclass. The class provides a number of methods that you can override to control which objects can be attached to one another, and the special effect of enacting out those attachments.

The Attachable class provides default handlers for AttachTo, Detach, DetachFrom, and TakeFrom, but it's relatively easy to define additional, customized action handlers in terms of these default actions using the standard 'remapTo' mechanism. For example, if you were defining a rope, you could define a TieTo action, and then map TieTo to AttachTo in your rope object.

The new classes PermanentAttachment and PermanentAttachmentChild are subclasses of Attachable for cases where you describe objects as attached in the story text, but you don't want to allow the objects to be detached from one another. Describing objects as attached tends to invite a player to try to detach them; these classes lets the objects provide customized responses to the player's attempts, without actually allowing the objects to come apart. (These classes aren't intended for mere components; the Component class is adequate for that. PermanentAttachment is for situations such as a ball attached with a string to a paddle, where essentially separate objects are conspicuously attached to one another.)

The new mix-in class PresentLater makes it easy to set up objects that aren't in the game world initially, but will appear later in response to some event. For example, you might want to create a hidden door that isn't part of the game world until the player casts the DETECT HIDDEN DOORS spell, or you might want to create a pile-of-leaves object that shows up at the foot of a tree after the player shakes the tree.

The traditional way of programming these sorts of objects was to create the object with a 'nil' initial location, then use moveInto() to move the object into the game upon the triggering event. PresentLater provides an alternative. To use PresentLater, you set up the object in its eventual location, as though it were going to be there from the start, but you add PresentLater at the start of the object's superclass list. During pre-initialization, the library will remember the object's starting location, and then move the object to 'nil', effectively removing it from the game world. Later, when you want to the object to appear, simply call makePresent() on the object, and it will automatically move to the eventual location that was noted during start-up.

The advantage of using PresentLater is that you define the object's eventual location as part of the object itself, as though it were an ordinary object; this means that you don't have to specify its eventual location as part of some method (in a moveInto call) elsewhere in the source code. Another advantage is that you can use the makePresentByKey() method to bring a whole set of related objects into the game world at once: you can give each PresentLater object a "key" property, which is just an arbitrary value you choose to identify a group of objects, and then bring all objects that have that key into the game world at once.

The new class Unthing represents the absence of an object. This class is useful for the occasional situation where a player is likely to assume that an object is present even though it's not. For example, sometimes an object becomes hidden, but a player might not notice that; rather than letting the regular parser error handle it, we can create an Unthing that explains that the object can no longer be seen. Unthing is a simple subclass of Decoration, with a customized default handling message.
The travelerDirectlyInRoom precondition object has been replaced with a new TravelerDirectlyInRoom class. This change allows the caller to create the precondition object with the full set of information it needs via the class's constructor. This change is probably transparent to existing game code, as game could would have little reason to use this precondition.
The class Thing now defines the method checkTravelerDirectlyInRoom(); the default implementation simply defers to the container. This ensures that connectors that are defined inside ordinary objects (for example, a hole in a bookcase) will properly ensure that a traveler is in the Thing's containing Room or NestedRoom before travel. In the past, the absence of this method meant that there were no conditions at all on a traveler's initial location in such cases.
The dropDestinationIsOutRoom precondition now moves the actor, rather than the traveler (to the extent that they differ), to the outer room when needed. (Since the actor is attempting to reach the outer room in these cases, it makes more sense to move the actor, rather than any vehicle the actor is inside.)

Similarly, the Floor object's SitOn and LieOn handlers now apply a precondition requiring the actor, rather than the traveler, to be in the room directly containing the Floor object.

The new VocabObject method getFacets() returns a list of "facets" of an object. These are other objects that share the same parsing identity; for example, the two sides of a door are facets of the same physical object, from a character's perspective, so each side would return a list containing the other side from getFacets(). In fact, the Door class overrides getFacets() to do just this.

The parser uses getFacets() to resolve an out-of-scope pronoun antecedent, if possible. If the player uses a pronoun, and the antecedent is out of scope, the parser calls the antecedent's getFacets() method, and looks for a facet that's in scope; if it finds an in-scope facet, the parser uses that facet as the replacement antecedent. For example, if you type OPEN DOOR, GO THROUGH IT, THEN CLOSE IT, the parser will now correctly resolve the second "it" to the door. In the past, the second "it" wasn't resolvable, because it referred to the side of the door that's in the other room and thus out of scope after the travel. Using the new facet list capability, the parser can now resolve the second "it" to the side of the door that is in scope.

(If more than one new facet of the object is in scope in these cases, the pronoun resolver will take the one that's most readily visible - that is, the one that has the most "transparent" sense path in the actor's visual senses. If all of the in-scope facets are equally visible, the resolver just picks one arbitrarily.)

By default, VocabObject.getFacets() simply returns an empty list. The Door and Passage classes override the method to return a list containing the linked object representing the other side of the door or passage, if there is one. The new MultiFaceted object returns the list of facet objects that it synthesizes; each facet instance of a MultiFaceted does the same thing.

You can customize getFacets() whenever you use multiple objects with a shared parser identity. One common case where you might want to do this is "object transmutation," where you substitute one object for another in order to effect a radical change in the setting. For example, you might have one object representing a wooden dining table, and another representing the pile of splintered wood that an enraged chef turns it into after the guests insult his cooking. In this case, you could override getFacets() in the table so that it returns the pile-of-splintered-wood object.

The class AutoMultiLoc has been removed, and its capabilities have been rolled directly into the MultiLoc class itself. All of the initialization properties and methods (initialLocationClass, isInitiallyIn, and buildLocationList) have been moved into MultiLoc, and they work the same as they used to. There really wasn't any need for a separate AutoMultiLoc class, since the two styles of initialization (enumerated and rule-based) can coexist quite easily in the single class.

Along the same lines, the DynamicMultiLoc class has been removed, and its functionality has been rolled into MultiLoc. In particular, the reInitializeLocation() method is now part of the base MultiLoc class.

If you have any AutoMultiLoc or DynamicMultiLoc objects in your existing game code, you should simply change the AutoMultiLoc or DynamicMultiLoc superclass to MultiLoc. The objects should then behave the same as they did before.

MultiLoc.moveOutOf() caused a run-time error due to a mismatched call to notifyRemove(). This is now fixed.
The new classes MultiInstance and MultiFaceted are useful for cases where you want to create an object in multiple locations, but MultiLoc isn't suitable. A MultiLoc is suitable when a single object is contained entirely and simultaneously in more than one location; when a single large object spans several locations, or when you simply want to duplicate a ubiquitous background object in several places, the new classes are better.

MultiInstance is good when you want to duplicate a ubiquitous background object in multiple locations: trees in a forest, the sun in outdoor locations, crowds in the street. MultiFaceted is for large objects that span many locations, such as rivers or long ropes. MultiLoc isn't ideal for these sorts of cases because the sense system treats a MultiLoc as a single object that's entirely in all of its locations at once; this means, for example, that if it's lit in one place, it's lit everyplace.

MultiInstance and MultiFaceted work mostly like MultiLoc from the programming perspective. Internally, though, rather than appearing itself in each of its locations, a MultiInstance or MultiFaceted creates a separate "instance" object for each of its locations. Each instance is an ordinary singly-located object, so the library creates one per location. The instance objects are instances of a template that you define as part of the MultiInstance or MultiFaceted object.

The new classes present the MultiLoc multi-location interface, so you can add and subtract locations as needed, using the usual routines: moveInto(), moveIntoAdd(), moveOutOf(). You can also set the initial set of locations as you would for a MutliLoc: you can set locationList to the list of initial locations, or you can define initialLocationClass, isInitiallyIn, and/or buildLocationList.

The instances of a MultiInstance or MultiFaceted are all essentially identical, so these classes are only good for relatively homogeneous objects. These classes aren't appropriate for cases where you want to present distinctive aspects of an object, such as the different sides of a large building, or distinctive individuals objects, such as different people in a crowd. When the individual parts are distinct, you're best off just creating separate objects for the different instances. The new classes are mainly for convenience in cases where you'd otherwise have to repeat an identical object definition in several locations.

The new pre-conditions dobjTouchObj and iobjTouchObj can be applied to the indirect or direct object (respectively) of a two-object action, to require that the one object can touch the other. This is useful for actions where the actor manipulates one of the objects directly, but manipulates the other object only indirectly using the first object.

For example, PUSH COIN WITH STICK would be a good place to use iobjTouchObj as a direct object precondition: the actor is manipulating the stick directly, so the actor has to be able to touch the stick, but the coin only needs to be reachable indirectly with the stick. Another example: PLUG CORD INTO OUTLET requires direct manipulation of the cord, but only the cord needs to touch the outlet, so this would be a good case for dobjTouchObj as a precondition on the indirect object.

In the class Thing, the default direct object handlers for the actions MoveWith, TurnWith, ScrewWith, and UnscrewWith now use the iobjTouchObj condition rather than touchObj condition. These verbs all generally model interactions where the direct object only needs to be reachable through the indirect object. The new class Attachable uses dobjTouchObj as a precondition on the indirect object.

To enable the new iobjTouchObj condition, the TouchObjCondition class can now handle a yet-unresolved source object during the verification stage. When the source object is nil, the pre-condition simply skips its verification stage. This allows a TouchObjCondition to be applied across objects for a two-object action: for example, it allows you to apply a condition to the direct object that the indirect object can touch it.

The new preconditions sameLocationAsIobj and sameLocationAsDobj let you require that a given object is in the same immediate location as the other object of a two-object command. You use sameLocationAsIobj as a precondition on a direct object, and sameLocationAsDobj as a precondition on an indirect object. Both of these condition objects are based on the class SameLocationCondition, which you can construct dynamically to require the target object to be in the same location as an arbitrary second object, which need not be directly involved in the command.

These conditions use the new Thing method tryMovingObjInto(obj), which tries to move the given object 'obj' into 'self'. This method is customized in Room, Container, Surface, and Actor to generate appropriate implied commands for those classes, and you can override it in your own objects as needed.

The new Actor method endConversation() effectively lets an NPC say GOODBYE of its own volition, ending a conversation without waiting for the player to leave or say GOODBYE. This is the complement of Actor.initiateConversation: it lets an NPC initiate the end of a conversation.

The new conversation-ending code endConvActor indicates that we're ending the conversation of the actor's volition.

The new ConvNode method noteLeaving() is called when the conversation node is about to become inactive. This is the complement of noteActive(). This method doesn't do anything by default, but instances can use it to trigger side effects when leaving the node.
The conversation manager's mechanism that keeps track of conversation responses has been changed to make it better able to handle multiple conversations on a single turn. In particular, the conversation manager now flags the start and end of each response text in the text stream. Because the "transcript" subsystem captures displayed text and defers its actual output until later, difficult synchronization problems arose if a game tried to trigger multiple responses from different actors on a single turn. This change should be transparent to existing game code.
TopicEntry has a new method, setTopicPronoun(), that tries to set a pronoun antecedent when the topic is matched. The default handleTopic() method automatically calls the new method.

It's not always possible for setTopicPronoun() to guess about an antecedent for a topic phrase match, because TopicEntry instances can match more than one game object, and topic phrases by their nature can refer to more objects than are present visually. The new method will set a pronoun antecedent for the topic if the topic phrase in the player's input yields one object when "intersected" with the TopicEntry's 'matchObj' list. The method first looks only at in-scope objects, then looks to the "likely" list, but only if there are no in-scope matches. If the intersection yields more than one object, the library doesn't set a pronoun antecedent at all, since the match is ambiguous.

Since this heuristic procedure can't always decide on an antecedent, you might occasionally want to set the antecedent explicitly in a particular TopicEntry. You can do this by overriding the TopicEntry instance's setTopicPronoun(fromActor,topic), in which you'd call fromActor.setPronounObj(obj), where 'obj' is the game object you want to set as the antecedent.

In the past, when an "again" command was used to repeat a command directed to a non-player character ("bob, go east"), the turn counter incorrectly advanced by two turns. This was due to an error in the mechanism that allows one actor to wait for another to finish a command. This is now fixed.

A separate problem prevented an undo savepoint from being created for an "again" command repeated a command directed to another character. Since no savepoint was created, typing "undo" after such an "again" command took back not only the "again" turn but the turn before it as well. The savepoint is now created properly.

Due to a bug, the conversation mechanism didn't enter a conversation (i.e., didn't show the "greeting protocol") if a DefaultTopicEntry was used for a response. This has been fixed.

As part of this change, the ActorTopicEntry class, which was introduced in the refactoring of the TopicEntry class introduced in 3.0.6l, has been eliminated. On further consideration, the bifurcation of TopicEntry into actor-associated and non-actor-associated subtypes wasn't very clean, since it implied a similar split for some of the other classes that work with TopicEntry, notably AltTopic and DefaultTopic. This was too unwieldy.

So, ActorTopicEntry has been removed. In its place, the "topic owner" abstraction, which was introduced in 3.0.6l, has been used throughout the base TopicEntry class to replace the need for an associated actor. This means that the base TopicEntry can do everything that it did before (and everything that ActorTopicEntry did before), but without any assumption that there's an associated actor. The getActor() method is still present, but it's now strictly for the convenience of game code; the library no longer assumes that topic entries to be associated with actors. The TopicEntry.getActor() method simply returns the topic owner if it's of class Actor, otherwise nil.

These changes should have no impact on existing game code, since the ActorTopicEntry was intended as an internal class structure only.

A bug in the TopicResolver class caused a run-time error for a command like ASK someone ABOUT ALL. This is now fixed.
When the ConsultAbout action picks a default consultable object based on the actor's last ConsultAbout command, the action now marks the defaulted object as such, which triggers the usual announcement of the default object.
The typographicalOutputFilter object (in en_us.t) is now a little smarter about what it considers sentence-ending punctuation. If a lower-case letter or a hyphen of some kind follows what the filter would otherwise take for sentence-ending punctuation, it doesn't treat it as a sentence ending. This gives better results in the common case of an exclamation point or question mark within a quoted passage: "'Who are you?' he asked." It also helps in some less common cases, such as when an exclamation point is embedded in a sentence, such as in an interjection set off by dashes ("And then -- oh no! -- I dropped it").
The PendingCommandInfo class has been refactored into a couple of subclasses for a cleaner class structure. PendingCommandInfo is now an abstract base class; the new concrete subclasses PendingCommandToks, PendingCommandAction, and PendingCommandMarker now implement the specialized behavior that was formerly embedded in the single class and handled conditionally. These classes are mostly for internal use in the library, so this should have no impact on any existing game code.
Some messages in BasicContainer have been modified slightly to remove the assumption from the base class that it's an object that can be opened. First, the new properties cannotTouchThroughMsg and cannotMoveThroughMsg give property pointers, referring to playerActionMessages properties, that specify the messages to use when an object cannot be reached or moved through the container's containment boundary. By default, these now refer to the new messages cannotTouchThroughContainer and cannotMoveThroughContainer, which don't say anything about the container being closed. Second, BasicOpenable class overrides these two new properties to refer to the original cannotTouchThroughClosed and cannotMoveThroughClosed messages. Third, the tryImplicitRemoveObstructor() method that was formerly in BasicContainer has been moved to BasicOpenable instead, eliminating the assumption in BasicContainer that the obstruction can be removed by opening the container.

Note that these changes are designed in such a way that objects based on both Openable and Container (either by mixing the two superclasses, or by using the library class OpenableContainer) should be unaffected. Objects based on Container or BasicContainer, without any Openable mix-in, should now behave more logically, in that they won't assume anything about being openable.

The touchObj precondition incorrectly reported two failure messages if an implied command failed trying to remove an obstruction to touch. The failure message of the implied command itself was shown, and a separate failure message explaining that the target object isn't reachable was also shown; in such cases, only the implied command failure should have been reported. The precondition now omits the redundant second message.
KeyedLockable is now more flexible about the "known key list" and who owns it. In the past, the "master object" of a linked lockable (the master object of a two-sided door, for example) always owned the known key list; this made it impossible to have separate keys operate the two sides of a door, and also created a dependency on which side of the door was the master. Now, KeyedLockable instead uses the local side's known key list if it has one, and only uses the master side's list if either the local side has an empty list or the master side has no list; only if the local side has an empty list, and the master side has a list, is the master side used. This makes initialization of two-sided doors much more flexible, because it no longer matters which side is the master for the purposes of the known key list.
In 3.0.6j, we introduced the notion of whether or not the status of a Lockable is known. The idea was that actors shouldn't just automatically unlock a door on trying to open it unless they have some reason to know the door is locked in the first place. Unfortunately, this mechanism took away the important playability convenience feature that automatically unlocks locked doors when it's obvious how to do so.

This mechanism is now replaced with something a little more subtle, which solves the same problem without any loss of convenience for the player. Instead of letting the original OPEN command fail on encountering a door that's locked but not previously known to be locked, the library now inserts a "testing" action into the sequence ahead of the implied UNLOCK. The "testing" action represents the actor's first attempt to open the lockable, as in attempting to turn a locked doorknob. This first phase fails, but instead of letting the rest of the command fail, we consider the actor to have immediately learned from the first phase that the object is locked, so we proceed directly to an implied UNLOCK action. The result looks like this:

>go north
(first trying the door and finding it locked, unlocking it, then 
opening it)

Note that the lockStatusObvious property of the former scheme still applies. If lockStatusObvious is true, then the extra "testing" phase is omitted, since we can tell just looking at the object that it's locked. Note also that the "testing" phase is only used when the object is actually locked; when it's not locked, the OPEN command succeeds, so there's no need to split the OPEN action into the two phases.

Since the lockStatusKnown mechanism was introduced fairly recently, and since it was intended to be a transparent internal mechanism anyway, it's unlikely that any existing game code will be affected by this change.

The Door class now uses a custom cannotTravel() message, rather than using the default message for the location. The message explains that the travel is not possible because the door is closed; the generic cannot-travel message for a room simply claims that the travel is impossible, which isn't as specific as we can be about it.
The Openable class has a new method, openStatus, that makes it easier to override the open/closed status addendum that's shown as part of the object's description. The openableContentsLister now calls this method to show the status; the default implementation just says "it's open" (or equivalent, adjusted for gender and number). An Openable can override this to customize the message as needed.
The Passage class's initialization of a two-sided relationship has been refined slightly. In the past, the non-master side of a two-sided passage explicitly set its master object's otherSide property to point to the non-master side. Now, instead of setting the property directly, the non-master side calls the master object's initMasterObject() method with the non-master side as the parameter; by default, initMasterObject() simply sets 'otherSide' to the other object. This makes it easier to customize the initialization for cases where more than two objects are involved in a passage relationship, or where the passage relationship is dynamic, by allowing the master object to directly control its own initialization.
In the Candle class, when the object runs out of fuel, it now displays its "burned out" message before actually cutting off its light source (by marking itself as unlit). In the past, the order of these operations was reversed, which caused the library to suppress the "burned out" message when the candle was the only light source. The message was suppressed because these operations are performed in a daemon that runs in the "sight" sense context of the candle itself; once the candle is marked as unlit, it becomes invisible when there's no other light source, and so any message displayed in its sight context is suppressed. By showing the message while the candle is still marked as lit, we ensure that the message will be seen as long as the candle is in view of the player character.
The Readable class now generates its "obscured" and "dim" default descriptions properly. A bug formerly caused a run-time error when attempting to read a Readable under these sense conditions.
The menu system now includes an automatic sorting mechanism, which sorts a menu's entries into a game-defined order each time the menu is displayed. The default initializeContents() method in the MenuObject class does the sorting, based on the new 'menuOrder' properties of the items in the menu. The 'menuOrder' property is an integer value giving the relative order of an item among its siblings. By default, 'menuOrder' returns the 'sourceTextOrder' value for the menu item, so the default ordering is simply the original ordering of the items in the source file.
In the English module, the new property isQualifiedName generalizes the function of the existing isProperName property. isProperName indicates that a 'name' property is "proper," meaning it's the name of a person or place - the kind of noun that's usually capitalized in English. Proper names don't require "qualification" when they appear in sentences, which basically means that they don't need articles like "the" or "a", because they're inherently definite. The new isQualifiedName property can be set to true for an object when the name is fully qualified, but isn't proper. By default, a proper name is considered qualified, but the two properties can be set independently as needed.

An example of a non-proper but qualified name is "your book." This isn't the proper name of the book, but the possessive pronoun makes it fully qualified, making it incorrect to add an article when using the name in a sentence.

In practice, at the moment, "qualified" and "definite" have the same effect, which is to omit any articles when the name is used in a sentence. The new property is intended to separate the two concepts in case there's a need to distinguish them in the game or in future library changes.

The English parser has a new bit of fine-tuning in its verb phrase chooser for a particular type of ambiguous phrasing. In certain sentences in English, a preposition can be interpreted either as part of a noun phrase or as a structural part of the verb; for example, DETACH STRING FROM BOX could be interpreted either as a verb with two objects (STRING as the direct object, BOX as the indirect object), or as a verb with a single object (STRING FROM BOX as the direct object). English speakers will almost always assume the two-object interpretation automatically in cases like this; English is such a strongly positional language that the verb phrase structure tends to dominate everything else in a sentence. In the past, the parser didn't care one way or the other, so when both interpretations were valid, the parser sometimes arbitrarily chose the much less probable single-object version. The parser now takes the verb structure dominance into account by preferring the intepretation with more "noun slots" in the verb phrase, whenever there's ambiguity.
The English parser's tokenizer now treats ampersands essentially as though they were alphabetic characters for the various kinds of "word" tokens. Ampersands have no other meaning to the standard parser, and show up every so often in names of things, so it makes sense for the parser to accept them as parts of words.

Note that an ampersand is treated as alphabetic, not as a punctuation mark. This has two implications that might be counterintuitive, at least to the extent that ampersands look to most people like punctuation marks. (In fact, they're not punctuation marks at all; they're actually ligatures of "et," Latin for "and," although the glyph has become so stylized in modern typefaces that it's hard to see that unless you know what to look for.) First, an ampersand is not a token separator, so a sequence like "T&V" will be read as a single token. Second, when an ampersand is surrounded by whitespace (or other token separators), so that it does count as a separate token, it'll be parsed as a "word" token, not as punctuation. For example, "Bob & Sons" is read as three "word" tokens. This means that you can use a lone "&" as an adjective or noun in defining an object's vocabulary.

In the English module, Thing.conjugateRegularVerb() incorrectly applied an "-ies" ending to verbs ending in a consonant plus a "y", such as "say". This affected routines like itVerb() and nameVerb(). The routine now uses a regular "-s" ending when a vowel precedes a terminal "y".
The parser now allows possessive pronoun adjectives ("her book") to refer back to the previous noun phrase in the same action. For example, ASK BOB ABOUT HIS HAT will treat "his" as referring to Bob. This is accomplished by first trying to find an earlier noun phrase in the same action that matches the pronoun in number and gender. If we find a match, we use it, otherwise we use the ordinary pronoun antecedent from a previous command. Note that the number/gender match ensures that we don't get overzealous in applying this: if we type ASK BOB ABOUT HER BOOK, we find that BOB doesn't match HER, so we look to the meaning of HER from a previous command.

As part of this change, the Action routine that was formerly called getReflexiveBinding has been renamed to getAnaphoricBinding. The new name better reflects the more generalized function, since it can be used for other kinds of anaphoric bindings besides reflexives. This is an internal method that is highly unlikely to be used in any game code, so this change should be transparent.

("Anaphor" is the linguistic term for any sort of reference to an earlier noun phrase in a sentence; a reflexive is a particular kind of anaphor that re-uses a noun phrase from an earlier "slot" in a predicate to fill another slot in the same predicate, such as HIMSELF in ASK BOB ABOUT HIMSELF. The reason reflexives exist in English and other languages is that they're unambiguously anaphoric: in ASK BOB ABOUT HIMSELF, HIMSELF can only refer to Bob, whereas the HIM in ASK BOB ABOUT HIM almost certainly refers to someone other than Bob from a previous sentence or clause. The name change in the method reflects the fact that there are other kinds of anaphoric constructs besides reflexives; in ASK BOB ABOUT HIS BOOK, the HIS refers anaphorically to BOB, but it's not reflexive.)

In Actor.executeActorTurn(), when a pending response is delivered, the actor now clears its memory of the pending response. (In the past, the pending response wasn't cleared, so an NPC with a pending response would keep delivering the pending response over and over.)
When AskFor changed to a TopicTAction in 3.0.6l, the Actor handler for GiveTo was affected but wasn't updated properly. The GiveTo handler changes a GiveTo command directed to another actor into an AskFor command (BOB, GIVE ME THE CROWBAR becomes ASK BOB FOR CROWBAR), so the AskFor change required this rephrasing to be cast in the new TopicTAction terms. This change wasn't made, so the GiveTo handler didn't work properly. This has now been fixed.
Actor.desc no longer shows the postureDesc as part of the description of an NPC. Instead, this is shown in examineStatus(). This change shouldn't affect any existing code; it merely moves around where the calls are made, but still makes all the same calls in the same sequence. The change makes it easier to override an Actor's 'desc' without changing the overall display format.
A bug in the sense system prevented a self-illuminating object (an object with a brightness of 1) from applying its self-illumination to its own interior surface; the illumination was considered external only. This was inconsistent with the normal sense-transmission rules, and it's now been corrected. A self-illuminating object is now considered to have an ambience level of 1 on both its inner and outer surface.
A subtle bug in the sense system caused ambient sense energy levels to be transmitted incorrectly into the interior of objects with non-transparent fill media. In particular, the sense system incorrectly applied the fill medium to the ambience level at the inner surface of an object as transmitted from the outside of the object. This was incorrect because it meant that a viewer on the inside of an object saw the ambient level to the object adjusted twice for the fill medium: once for the incorrect adjustment at the inner surface, and once more for the path from the viewer, through the fill medium, to the inner surface. This has been corrected: the ambient level arriving at the inner surface of an object from outside the object is now calculated without any adjustment for the fill medium, and any further transmission inward is then adjusted for the fill medium.

This problem showed up by making an object invisible from its own interior when the object had a non-transparent fill medium, and the light coming in from outside wasn't bright enough to penetrate the fill medium twice. For example, a transparent booth filled with attenuating fog, with external normal illumination (brightness 3), was not visible from its interior because of the double traversal of the fog. Such a booth is now visible from its interior.

When looking for an object's default associated Noise or Odor, the Thing methods getNoise() and getOdor() now only consider objects with an active "presence" in their sense. That is, getNoise() will only return a Noise object with a non-nil soundPresence property value, and getOdor() will only return an Odor with a non-nil smellPresence. This makes it easier to turn an object's associated sense data on and off, since you can simply use the presence properties of the Noise and Odor objects.
In 3.0.6j, CollectiveGroup stopped matching singular phrases and phrases with quantities specified (as in "five coins"). This behavior hasn't changed, but it's now easier to control, with the new method isCollectiveQuant(). This method returns true if the given "required quantity" allows using the collective, nil if not. By default, this returns true if and only if there's no specific quantity needed (in which case the quantity parameter is nil).
RandomTextList and ShuffledTextList can now handle arbitrary event entries in their lists, using the same rules as the base EventList class. (In the past, only strings and nil values were allowed in these specialized subclasses, but they now use the base class handling to carry out each event, so they can now handle any sort of event the base class can.)
3.0.6l

Released October 4, 2003

The actor knowledge and "sight memory" systems have been revamped to make it easier to keep track of individual knowledge for different actors. A few key interfaces have changed, so existing games will have to make corresponding changes.

First, the main interface changes:

Note that the "stat" parameter has been removed from the two "set" methods. This parameter was essentially superfluous, since it's rare to the point of non-existence for a game to want to mark something as un-seen or unknown after it's been previously seen or known; the new methods elide this parameter and act like the old methods acted with the parameter set to true.

You have a choice of how to update your existing game code to the new interfaces. The first option is to do the search-and-replace operations in the list above throughout your game's source code; this is the recommended option, because it'll make your code consistent with future documentation. The second option is to modify the VocabObject class to reinstate the old methods, defining them in terms of the new ones:

modify VocabObject
  seenBy(actor) { return actor.hasSeen(self); }
  setSeenBy(actor, flag) { actor.setHasSeen(self); }
  isKnownBy(actor) { return actor.knowsAbout(self); }
  setKnownBy(actor, flag) { actor.setKnowsAbout(self); }
;

Second, how do the changes facilitate separate NPC knowledge tracking? The change here is that the Thing and Topic 'seen' and 'isKnown' properties that underlay the old system are still used, but are now only defaults for tracking the seen/known information. The actual properties used to track the information are determined on an actor-by-actor basis, using the new Actor.seenProp and Actor.knownProp properties.

By default, Actor defines seenProp as &seen, and knownProp as &isKnown. This means that, by default, there's only one "global" set of knowledge. To track an NPC's knowledge individually, simply set seenProp and/or knownProp to new properties unique to that actor. For example:

bob: Person
  // ... other definitions...
  seenProp = &bobSeen
  knownProp = &bobKnown
;

This specifies that 'bob' will individually track its knowledge, separately from any other actors. When you call bob.setKnowsAbout(obj), the library will examine obj.bobKnown and obj.bobSeen, because those are the properties that track bob's knowledge. Note that this individual tracking is completely automatic once you define seenProp and knownProp, since the Actor methods hasSeen, setHasSeen, knowsAbout, and setKnowsAbout all use seenProp and/or knownProp to do their work.

Note that because the default values in Actor for seenProp and knownProp are (respectively) &seen and &isKnown, everything works roughly the same way it did before if you don't override these properties. In particular, if you don't care about tracking individual NPC knowledge, which typical games probably won't, you only have to worry about 'seen' and 'isKnown' as before. So, if you want to initialize an object as pre-known, before the start of the game, simply set isKnown=true for that object as in the past.

Third, a minor enhancement: the GIVE and SHOW iobjFor() handlers in Actor now automatically mark the object being shown, and its visible contents, as having been seen by the actor being shown the object. For games that track NPC knowledge separately, this will ensure that the target of a GIVE or SHOW takes note of having seen the object in question.

A new class, Consultable, can be used to implement things like books and file cabinets: inanimate objects in which an actor can look up a topic, to find some information. This class works almost exactly like the Actor conversation system. Another new class, ConsultTopic, serves as the TopicEntry subclass for Consultables. Simply create one or more ConsultTopic objects, and locate them within the Consultable, to populate the consultable object's database of responses.

In support of the new Consultable class, the TopicDatabase and TopicEntry classes have been refactored a bit. In particular, these two classes no longer assume that they're associated with an actor, and no longer assume that they're involved in conversation. The parts of the former implementations that made assumptions about actor or conversation associations have been moved into a pair of new subclasses, ActorTopicDatabase and ActorTopicEntry. The existing classes that were based directly on TopicDatabase and TopicEntry are now based on the new ActorXxx subclasses instead. For almost all existing game code, this change should be entirely transparent, because the final subclasses that most games use still have the same names and work the same way they did before; the only changes are some internal reshuffling to abstract out most of the functionality into the new base classes.

In addition, the TopicResolver class has a new subclass, ConvTopicResolver, that's used for the conversational actions (ASK ABOUT, TELL ABOUT). The base TopicResolver doesn't differentiate at all among topics; it simply considers everything to be an equally good match. The ConvTopicResolver differentiates topics into three sublists, as it did in the past: objects that are in physical scope or are known to the actor performing the command; objects that are "likely" topics, as indicated by the performing actor's isLikelyTopic(); and everything else.

The ConsultAbout action in the English module now adds grammar for some incomplete forms, such as "look up (topic)". If the player enters one of these incomplete forms, the parser will prompt for the missing object in the usual fashion.

Similarly, when the direct object is missing from a CONSULT ABOUT command, the default is the last object consulted by the same actor, as long as that object is still visible. The new Actor property lastConsulted keeps track of the last object consulted; the new Actor method noteConsultation() sets this property. The Consultable class calls gActor.noteConsultation(self) in its ConsultAbout action() handler. This saves the player some typing, since they can leave out the object to be consulted when they're looking up a series of topics in the same consultable (they can type simply LOOK UP BOB, for example).

The TopicEntry scoring system now uses nil to represent a non-match. This means the valid scoring range now includes zero and negative values, which in turn means that it's no longer a problem if matchScoreAdjustment values reducing scores to zero or below. This makes it easier to use score adjustments, since there's no longer any need to worry about interactions between score values and adjustment values taking a final score out of range.
The AskFor action is now a TopicTAction instead of a TIAction. That is, the indirect object is now a topic rather than an ordinary resolved object. The TIAction implementation was never really appropriate, since ASK FOR conceptually needs topic-oriented rules for the indirect object: one needs to be able to ask for things that aren't in scope, as well as for abstract topics (ASK FOR HELP, ASK FOR DIRECTIONS).

This change should have little or no impact on existing games.

The new TopicEntry subclass AskAboutForTopic can be used to make a single topic entry respond to either ASK ABOUT or ASK FOR for a given game object or objects (read the name as "ask about/for topic"). Similarly, AskTellAboutForTopic can respond to ASK ABOUT, TELL ABOUT, or ASK FOR (read the name as "ask/tell about/for topic," although the TELL FOR combination implied in that reading doesn't make any sense, obviously).
The limitSuggestions property now applies to ActorState objects as well as to ConvNode objects. Suggestions are effectively arranged into a hierarchy: the top level of the hierarchy is the ConvNode, the next level is the ActorState, and the bottom level is the Actor. The full list of suggestions consists of the ConvNode's suggestions, plus the ActorState's suggestions, plus the Actor's suggestions. However, if limitSuggestions is true at any level of the hierarchy, then suggestions below that point are not included in the full list. So, if the ConvNode's limitSuggestions property is true, only the ConvNode suggestions are included. If the ConvNode's limitSuggestions property is nil, but the ActorState's limitSuggestions property is true, then the ConvNode and ActorState suggestions are both included. If limitSuggestions is nil for both the ConvNode and ActorState, then the suggestions at all three levels (ConvNode, ActorState, and Actor) are included in the full list.
The new class SuggestedTopicTree makes it easy to create a tree of AltTopic alternatives that acts as a single suggested topic.

Normally, when you create a tree of AltTopic alternatives, you can make each AltTopic a separate suggestion. Each alternative is effectively an independent topic for suggestion purposes, so asking about one doesn't satisfy the PC's curiosity about any of the other alternatives. This means that the game might make the same suggestion several times, as the different alternatives become active.

In many cases, it's better to treat the whole group of alternatives as a single suggestion, so that the suggestion is only made once (or only as many times as desired) for the whole set. This is what SuggestedTopicTree is for. To use this class, simply add SuggestedTopicTree to the superclass list for the main TopicEntry (that is, the TopicEntry at the root of the tree: the one containing all of the AltTopic alternatives).

Note that the new TopicEntry property altTalkCount keeps track of the number of invocations for an entire alternative group. This property is kept in the outermost TopicEntry for an AltTopic group, and is incremented each time the outermost TopicEntry or any of its AltTopic objects is invoked (via ASK ABOUT, TELL ABOUT, etc). SuggestedTopicTree uses this new counter to determine if the PC's curiosity has been satisfied for the set of alternatives as a group.

The former ConversationManager method setRespondingActor() has been renamed to beginResponse(), and a new method finishResponse() has been added. Each call to beginResponse() should have a corresponding call to finishResponse().

This change allows the conversation manager to defer setting the new default ConvNode in the responding actor until after the response has been finished. This ensures that a ConvNode's noteActive() method will not be invoked in response to a <.convstay> tag. In the past, the conversation manager immediately transitioned the actor to the default next ConvNode at the start of showing the response, so if the response contained a <.convstay> tag, this caused a transition back to the starting ConvNode, and thus a call to noteActive() on the ConvNode object. The new scheme ensures that the actor's ConvNode won't change at all when processing a <.convstay> tag.

The AgendaItem class has a new method, resetItem(), that is invoked each time the item is added to an actor's agenda (via Actor.addToAgenda()). By default, this method sets the item's isDone property to nil, as long as the property isn't a method. This makes it easier to re-use agenda items, since you don't have to worry about resetting isDone explicitly.
The Surface class now uses a custom Lister, surfaceInlineContentsLister, to show its in-line contents listing. The new lister shows the in-line contents using the format "(on which is...)". In the past, Surface simply inherited Thing's in-line contents lister, which phrases things in terms of containment ("(which contains...)"), which isn't quite right for surfaces.

For naming consistency, the Lister formerly named keyringListingContentsLister has been renamed to keyringInlineContentsLister. The old name was a bit odd and didn't properly call attention to the "in-line" aspect.

Whenever an object's contents are listed via EXAMINE, LOOK IN, or OPEN, the library now automatically marks all of the object's visible contents as having been seen. Only contents that are actually visible to the character performing the command will be marked as seen, but it doesn't matter whether or not the contents are mentioned at all in the description of the object.
The new Thing method useSpecialDescInRoom(room) lets an object specify whether or not its special description should be shown in a LOOK AROUND description. By default, this simply returns useSpecialDesc(). In some cases, it might be desirable for an object to show its special description only when a container is examined, but not in a room description; this methods lets the object control this.

The new Thing property suppressAutoSeen can be used to suppress the library's automatic "seen" marking. By default, the library automatically marks every visible object as having been seen whenever a LOOK AROUND command is performed. The library also automatically marks as seen every visible object within a container when the container is explicitly examined (with EXAMINE, LOOK IN, or OPEN, for example).

suppressAutoSeen is nil by default. If you set it to true for an object, then the library will not mark the object as having been seen in any of the standard cases, even when the object is visible. The game must mark the object as having been seen by explicitly calling setSeenBy(), if and when desired.

DistanceConnector now derives from Intangible as well as SenseConnector. This makes it easier to use DistanceConnector, since it's no longer necessary to mix it with a Thing subclass. Each distance-connector object needs to derive from Thing so that it fits into the normal sense model, but these objects will almost never have any physical presence in the game; Intangible fills both of these needs.
The DistanceConnector template in adv3.h, which sets the locationList property, has been changed into a generic MultiLoc template. This lets you initialize any MultiLoc object's location list by specifying the list of locations after the class name when defining the object.
The Room template in en_us/en_us.h now accepts a room that defines only a 'name' property; the 'desc' property is now optional. This lets you use the template to define the room name even if you want to define a complex method for the 'desc'.
A bug in the Sense class incorrectly considered objects to be out of range of the 'smell' and 'sound' senses when the objects had distant, obscured, or attenuated sense paths. This has been corrected.
A Door object's getDoorOpenPreCond() can now return nil, if it's not desirable to use a precondition for opening the door.
The BasicChair, BasicBed, and BasicPlatform classes now include a touchObj precondition for the SIT ON, LIE ON, and STAND ON commands.
Where it makes sense, the preconditions that report failures with messages along the lines of "You must open (something) first" now set the pronoun antecedent to the object mentioned. This makes exchanges like this work as one would expect:

  >north
  You must open the door first.

  >open it
In the hint system's Goal class, the new properties openWhenKnown and closeWhenKnown let you tie a hint topic to the player character's knowledge of a Topic or Thing. If you set a Goal object's openWhenKnown to refer a Topic or Thing, then the Goal will become "open" as soon as the Topic or Thing becomes known to the PC. Likewise, if you set closeWhenKnown to a Topic or Thing, the goal will become "closed" when the Topic or Thing becomes known.

The new Goal properites openWhenRevealed and closeWhenRevealed let you tie a hint to a <.reveal> tag. You can set these properties to (single-quoted) strings giving <.reveal> tags to test.

The Goal class has two new methods, openWhen and closeWhen, that compute the conditions for opening and closing the hint topic, respectively. These methods isolate the conditions that were formerly included in-line in the updateContents() method.

Isolating the conditions in separate methods makes it easier to use 'modify Goal' to add new custom open/close sub-conditions. For example, suppose you wanted to add a pair of new custom Goal properties that open and close hint topics based on a Thing being moved for the first time. You could do this like so:

  modify Goal
    openWhenMoved = nil
    closeWhenMoved = nil
    openWhen = (inherited || (openWhenMoved != nil && openWhenMoved.moved))
    closeWhen = (inherited || (closeWhenMoved != nil && closeWhenMoved.moved))
  ;
The new library function intOrdinal(n) returns a "numeric ordinal" representation of the number: '1st', '2nd', '3rd', and so on. (Thanks to Søren J. Løvborg for suggesting this and providing a sample implementation.)

Along the same lines, the new library functions spellIntOrdinal(n) and spellIntOrdinalExt(n, flags) return fully spelled-out ordinals ('first', 'second', 'third', etc). The 'Ext' version takes the same bit-flag values as spellIntExt().

The library no longer considers the player character to be an antecedent to a third-person pronoun when the game itself refers to the PC in the first or second person (as determined by the PC Actor's referralPerson property). In the past, typing X ME and then X IT examine the player character twice. This will no longer happen, except in third-person games, since the first X ME won't set ME as an antecedent for HIM, HER, IT, or THEM. This isn't especially important for IT and THEM, but it can be for HIM and HER if the PC has a specified gender.
The library now treats HIM and HER a little differently in disambiguation responses.
The English library module defines a few new methods related to pronouns and gender. The new Thing methods canMatchHim, canMatchHer, canMatchIt, and canMatchThem determine if an object can match these individual pronouns. By default, these simply return true if the corresponding isHim/isHer/isIt flags are true. Actor overrides these so that they return true if the inherited version returns true and the actor can be referred to in the third person, which is determined by testing the additional new Actor method canMatch3rdPerson. The new canMatch3rdPerson method returns true if the actor isn't the player character, or the game refers to the PC in the third person.
The former Actor methods canTouch(obj) and findTouchObstructor(obj) have been moved to Thing. There wasn't any need for these methods to be special to Actor, and it's sometimes useful to establish reachability between arbitrary non-Actor objects, so these are more appropriate for Thing than Actor.
The new class TouchObjCondition implements a generic object-to-object reachability condition. This is similar to the existing touchObj pre-condition, but allows for arbitrary source objects, whereas touchObj can only test to see if the current gActor can touch an object. touchObj is now a subclass of TouchObjCondition.
In the past, throwing an object at a MultiLoc object, or at a component within a MultiLoc object, didn't work properly. This has been corrected.

In order to allow throwing at MultiLoc objects, the interface to a Thing method, getDropDestination, has been altered slightly. This method now takes an additional parameter giving the "sense path" that was used in the operation that's seeking the drop destination. The new sense path parameter is a list in the same format returned by Thing.selectPathTo. This path information allows getDropDestination to determine whence the MultiLoc object was approached - that is, it allows a MutliLoc object to find out which of its containers or containment peers was last traversed to reach the object. This path can be nil, but when it's supplied, it tells us how we're approaching the drop destination.

Note that this change slightly affects the interfaces to several Thing methods: getHitFallDestination, throwTargetHitWith, stopThrowViaPath, throwViaPath, and throwTargetCatch. In all of these routines, the former 'prvCont' parameter is now replaced with the 'path' parameter. Similarly, the processThrow method passes the path rather than the previous container.

In the English grammar, abbreviated words that include periods in the abbreviation (as in "Main St." or "J. Pretensions") are now treated a little differently, to ensure that abbreviated words don't have any bad interactions with unabbreviated equivalents. This change should be completely transparent to existing game code.

First, the tokenizer no longer treats an abbreviation as a single token that includes the period, but rather as two separate tokens: one for the word itself, and a separate token for the period. The period is entered not with the ordinary punctuation token class, but with the new 'tokAbbrPeriod' class. Second, wherever ordinary noun tokens were allowed in the grammar, the grammar now instead accepts the new subproduction 'nounWord'. This new production matches an ordinary noun token, or a noun token followed by a tokAbbrPeriod token. Third, and similarly, the existing 'adjWord' production now accepts an adjective token followed by a tokAbbrPeriod token. Fourth, the vocabulary initialization (VocabObject.initializeVocabWith, which parses the vocabWords_ string) now enters each token that ends in a period with the period, as it did, but also without the period. So, for example, the vocabWords_ string 'main st.' will create an adjective 'main', a noun 'st.', and a second noun 'st' (in other words, 'st' is entered in the dictionary both with and without a trailing period).

This corrects a problem that could have occurred with the old scheme if the same word was used abbreviated and unabbreviated. For example, if 's.' was used as an abbreviated word (for a string like 's. main st.', for example), and 's' was also entered in the dictionary as a verb (which it is by default, for the South action) the command "s." was not properly handled. The problem was that the tokenizer would see that "s." was entered in the dictionary with the period, so it tokenized it with the period. This precluded interpreting it as two tokens, 's' and '.', so the 's' was not matched to the verb, hence the command was rejected as not understood.

A useful side effect of this change is that each abbreviation is now automatically entered in the dictionary without its period. This should be helpful to players who choose to avoid the extra typing of entering the periods in abbreviations.

The new Hidden class can be used to implement objects that are present but not seen. This is useful for objects that are too inconspicuous to be noticed, or are positioned in such a way that they can't be readily seen (but in such a way that the ordinary sense mechanism would think the object was visible).
The BulkLimiter class now parameterizes all of the messages that were previously coded directly as failure reports. The new BulkLimiter properties tooLargeMsg, becomingTooLargeMsg, and becomingTooFullMsg join the existing tooFullMsg in specifying the various failure reports; these properties in turn contain property pointers that refer to messages defined in the playerActionMessages object. This makes it easier to create "cosmetic" subclasses of BulkLimiter that define customized types of containment relationships.
The new macro askForTopic(action) allows a single-object TAction to ask for a topic phrase and retry the command as a TopicTAction. This allows, for example, a CONSULT action with no topic specified to ask for a topic and turn itself into a CONSULT ABOUT action.

To enable this change, the parser includes a new grammar production, EmptyTopicPhrase. This new production represents a topic phrase that requires an interactive response from the player.

In the past, it wasn't possible to use askForDobj() to convert a LiteralAction into a LiteralTAction, or a TopicAction into a TopicTAction, by requesting a missing direct object from the player. This was due to an oversight in the library, which has now been corrected.

Another similar, though more subtle, oversight made it impossible to use askForDobj() to convert a TAction into a TIAction. This is an unusual scenario, because the usual way to perform this conversion would be through askForIobj(): it's almost always the indirect object that's missing in these cases. However, there are cases where a missing direct object is possible; for example, we might want a full-fledged SPRAY verb that takes only one object, as in SPRAY AIR FRESHENER, but also have a two-object form for things like SPRAY SINK WITH DISINFECTANT, in which it's the direct object that's missing from the two-object form when we type SPRAY DISINFECTANT. (This particular example is a bit contrived, since we could just as well have defined the verb as SPRAY DISINFECTANT ON SINK, but even so, we don't want to be forced to find such a rephrasing.) In these cases, askForDobj() can now be used. When a TAction is retried as a TIAction with askForDobj(), the direct object of the TAction now becomes the indirect object of the TIAction; this is plainly the only way it can be, since we know the direct object is missing by virtue of the fact that we're asking for it.

The interface of the method Actor.trackFollowInfo() has been changed slightly to improve its flexibility. In the past, this method assumed that the current action was actually performing the travel; now, the method instead takes an additional argument to eliminate this dependency. The new third argument gives the starting location of the travel; this is normally simply the location of the actor traveling, but could be something else; for example, when the actor is inside a vehicle, then the starting location is the vehicle's location.
The new class NestedRoomFloor can be used for convenience in creating an object that serves as the floor of a nested room.
The Floor class now accepts STAND ON commands. STAND ON FLOOR is simply remapped to STAND UP.
The BurnWith action now resolves its direct object first. This is the better resolution order for this action, since it allows the direct object to set the scope for choosing the indirect object, which is likely to be omitted and thus require a suitable default to be chosen.

In addition, the FireSource class now declares it illogical to burn an object using itself as the fire source. It's common for a Candle to also be a FireSource; this change prevents such an object from being chosen as its own default fire source in a command like LIGHT CANDLE, which would cause an infinite loop as we tried to light the candle with itself, which would require first lighting the candle, which would default to lighting it with itself again, and so on.

The FireSource and Matchstick classes have also been fine-tuned to work together a little better. The FireSource class now specifies a logicalRank of 150 when used as the indirect object of BurnWith. The Matchstick class uses a logical rank of 160 when lit and 140 when not lit. This makes a matchstick an especially logical choice for starting a fire under all conditions, but makes a non-matchstick an even better choice when it's already burning. A lit match trumps everything (160), next best is an already-lit non-matchstick FireSource (150), and when there's nothing else around that's burning, a matchstick will still be a good choice (140). This ensures that we don't light a new match when there's another fire source readily at hand, but at the same time ensures that we'll default to using a match when no better choice is available.

When an implicit action is interrupted with an interactive prompt, the announcement of the implicit action is now phrased as "trying" the action. This is along the same lines as the change in 3.0.6k that phrased failed implicit actions as "trying"; since an interactive prompt interrupts an implied action, it makes more sense to refer to the implied action as being merely attempted at this stage.

Internally, the announcement generator distinguishes between failed actions and actions interrupted for interactive input, so it's a fairly easy matter to customize the types of messages separately. To customize the "asking" case separately from the "trying" case, customize the askingImpCtx object; in particular, override its buildImplicitAnnouncement() method and its useInfPhrase property as needed. The default in the library is to use the same message style for both cases.

Due to a bug in the library, using "exit" to abort an action from within a check() handler did not properly mark the action as failed within the transcript, which meant that the "trying" form of an implicit action announcement was not generated. This has been corrected.
In the past, when an implied action was remapped to another action (via remapTo), the normal announcement of the implied action wasn't generated. This has been corrected: the announcement will now be shown as usual.
The parser is now a little smarter about excluding items with ALL EXCEPT in cases of simple remappings. In many cases, remapTo is used to create simple object-to-object synonyms for certain verbs; for example, OPEN DESK might be remapped to OPEN DRAWER when it's obvious that the two commands mean the same thing. In these cases, when an object is excluded from the ALL list with EXCEPT, the parser will now look for these simple synonym remappings, and it will treat them as equivalent. So, if OPEN DESK is remapped to OPEN DRAWER, and the player types OPEN ALL BUT DESK or OPEN ALL BUT DRAWER, then the desk and drawer will be excluded from the ALL list.

Note that the parser can only exclude synonym remappings when they're remapped using the remapTo (or maybeRemapTo) mechanism. Remappings that are done using replaceAction() or the like can't be detected as synonyms, so they won't be excluded. Remappings that aren't simple synonyms won't be excluded, since remapping to different verbs or word orders changes the meaning of the command enough that the EXCEPT list shouldn't exclude the new objects. For example, if OPEN DOOR remaps to PUSH BUTTON, because a button is used to operate a mechanical door, OPEN ALL BUT BUTTON would not exclude the remapping to PUSH BUTTON, since we didn't say not to PUSH the button, only not to OPEN the button.

The standard "weak vocabulary" checking in Thing now takes into account truncatable vocabulary words, as well as any other special dictionary comparison rules, by using the main dictionary's string comparator to check for weak tokens. Note that this routine uses the global property languageGlobals.dictComparator, so if the game ever changes the main dictionary's string comparator object, it should also change this global property at the same time.
The Topic class now specifically disallows sensing a topic object with any physical senses. This ensures that a topic object will never be in any physical scope, even if the object is part of a containment hierarchy. (It's sometimes convenient to include a topic as part of a containment hierarchy in order to associate the topic with a physical game world object.)
The possessive qualifier resolver (PossessiveResolver) now considers an object to be in scope in a possessive phrase only if the object has the property canResolvePossessive set to true. This property is true by default for all Things and nil by default for all Topics. In other words, by default, possessive phrase qualifiers can only be resolved to physical game world objects, never to abstract topics.
3.0.6k

Released August 17, 2003

The new TopicEntry method isMatchPossible() tries to guess whether or not the TopicEntry can be matched by any input currently. This question is unanswerable in general, because it's asking if there exists any input, out of the infinite set of possible inputs, that will match the topic entry. However, the TopicEntry subclasses use heuristics to provide an answer that's right in most cases. In particular, the ASK and TELL entries consider themselves matchable if any of their matchObj objects are either known to the player character or are simply in scope; the GIVE and SHOW entries are considered matchable if any of their matchObj objects are in scope; and YES, NO, and special topic entries are always considered matchable. If a game creates a custom matchTopic() method, it might want to customize isMatchPossible() to match the custom match condition.

The SuggestedTopic method isSuggestionActive() uses this new method to hide suggestions that aren't currently matchable. This provides an important benefit for the most common cases: topics won't be suggested until the player character knows about them. This avoids showing suggestions for things the player shouldn't even have heard of yet.

To facilitate this change, isSuggestionActive() now takes an additional argument, giving the list of in-scope objects. This avoids the need to repeatedly compute the scope list when scanning a large list of suggested topic objects, which is important because the scope calculation is complex and can be time-consuming.

Some important terminology for TopicEntry objects has been changed: where we formerly referred to a TopicEntry as "known," we now refer to it as "active" instead. The old terminology implied that TopicEntry objects model NPC knowledge; this was misleading, though, because the availability of a TopicEntry often depends on PC knowledge as well as NPC knowledge, or what the NPC wishes to reveal rather than what the NPC actually knows, or various other things. The new terminology removes the implication that NPC knowledge in particular is being modeled.

This change requires new names for several TopicEntry properties: isKnown is now isActive; checkIsKnown is now checkIsActive; and topicGroupKnown is now topicGroupActive.

Game code that uses TopicEntry objects will have to rename isKnown to isActive in the object definitions. The other renamed properties might have to be changed as well, of course, but these are less likely to appear in game code.

The travel precondition mechanism has been tweaked a little to make it more flexible for handling nested-room situations. In the past, each travel connector was responsible for providing a precondition applied whenever an actor traversed the connector, via the travel connector's actorTravelPreCond(actor) method. This formerly returned the actorStanding precondition. While this was appropriate in most cases, since it ensured that the actor wasn't sitting in a chair or anything like that, it didn't make for easy customization for specialized nested rooms or other unusual situations.

The change is that TravelConnector.actorTravelPreCond(actor) now returns a new precondition, actorTravelReady, instead of actorStanding. The actorTravelReady precondition abstracts the travel condition by letting the actor's immediate location handle it. In particular, it calls three new BasicLocation methods to carry out the precondition: isActorTravelReady(conn), tryMakingTravelReady(conn), and notTravelReadyMsg. By default, these methods do exactly what actorStanding does: they require the actor to be standing up before travel. However, a location can override any or all of these. For example, a particular nested room might want to use a command other than "stand up" to get the actor out of the nested room in preparation for travel; the nested room could override tryMakingActorTravelReady() in this case to carry out the desired implied command.

Note that the travel connector to be traversed is an argument to the first two of the new methods. This allows the actor's current location to impose different requirements on travel via different connectors. For example, it might be necessary to climb down from a ladder before leaving via any connector except for a window high up on the wall, which can only be reached when on the ladder.

The new Action method cancelIteration() lets you tell cancel further iteration of the current action, if multiple objects are involved in the action. You can call this method on the current gAction object during the 'check' or 'action' phases of execution. Once the method is called, the action will complete the execution cycle for the current object as normal, but it will then act as though the current object were the last object of the command.

Note that this new method does not have any effect on the execution of the command for the current object; in particular, it doesn't interrupt the execution flow the way 'exit' and the like do. Instead, this method simply sets a flag in the current Action object telling it to ignore any additional objects it would otherwise iterate over. If you want to jump out of the execution cycle in addition to canceling further iteration, simply use 'exit' (or one of the similar signals) after calling this method. Note also that this method obviously can't cancel the iteration for any prior objects in the iteration.

In the status-line and menu modules, there were some unnecessary "flush" operations (on banners and on the main window). Most of these were placed before "sizeToContents" calls on banners, which is unnecessary because that operation automatically flushes the underlying output stream anyway. These extra calls were not only unnecessary but were also undesirable, because the explicit flush operations always update the display immediately in addition to flushing internal text buffers. This made the display flicker annoyingly at times. The extra flushes have been removed, which makes the display updating faster and eliminates the flicker in the common cases covered by the changes.
When a MenuLongTopicItem is part of a series of "chapters" (as indicated by the isChapterMenu property), and the user navigates directly from one chapter to the next, the menu system keeps the banner configuration for the chapter display throughout the change to the new chapter. In the past, the menu system removed the banner and then immediately restored it, which caused some visual flicker. This change eliminates the flicker during the chapter change.
The English implicitAnnouncementGrouper implementation now uses pronouns, where appropriate, to refer to repeated objects when it groups a series of announcements. It frequently happens that the same object appears more than once when a series of implied actions is performed; this new phrasing makes the announcement text shorter and makes it read more naturally. For example, rather than saying "first unlocking the door, then opening the door", we'll now say "first unlocking the door, then opening it".
The implicit action announcement mechanism now phrases the announcement for a failed action a little differently than for a successful action. When an action fails, the message now reads "(first trying to ...)". The old phrasing was a little awkward at times, because the announcement describes the action as being performed, and then the (usually) very next message says just the opposite, that the action wasn't performed at all because it failed. Depending on the wording of the failure message, the combination could even be misleading on occasion, since the suggestion in the announcement that the action was performed could lead to a player assuming that the failure message must refer to something else.

To enable this change, the interface to the libMessages method announceImplicitAction() has changed slightly. The method now takes a "context" parameter, which is an object of class ImplicitAnnouncementContext The context contains information on the format of the message to be generated.

Also, the implicitGroupTransform internally scans for failure messages that apply to implied action announcements, and rewrites the implied action announcements into the new format when failures are noted.

Finally, the implicitAnnouncementGrouper object implementation in the English module does some additional grouping, to render a series of "trying" messages as readably as possible. When a series of consecutive "trying" messages appears, the grouper combines them under a single "trying" phrase, as in "first trying to unlock the door and then open it."

Several small internal changes have been made in the English library module.

First, the new method getInfPhrase() returns a string giving the full action description in infinitive form. This returns a phrase such as "open the box" or "unlock the door with the key". The verb is always in the infinitive form, but note that the English infinitive complementizer, "to", is not part of the returned phrase; this is because an infinitive is used without a complementizer in certain contexts in English, such as with auxiliary verbs (such as "might" or "should").

Second, the new method getVerbPhrase(inf, ctx) returns the full verb phrase in either the infinitive form ("open the box") or the present participle form ("opening the box"). In English, the two forms are equivalent except for the verb ending, so the code to generate both forms can be easily consolidated into a single routine. The subclasses of Action override getVerbPhrase() as needed to provide appropriate phrasings. The context object 'ctx' is optional; if it's not nil, it should be an instance of the new class GetVerbPhraseContext. If the context object is provided, the routine uses it to keep track of pronoun antecedents for the verb phrase; the returned verb phrase will use a pronoun if one of its objects matches the current antecedent, and the routine will set the antecedent in the context for the next verb phrase that uses the same context. The context is useful if you're generating a series of verb phrases that you're going to string together into a single sentence; by using pronouns to refer to repeated objects, the sentence will be shorter and will read more naturally.

Third, the implementation of getParticiplePhrase() now simply calls getVerbPhrase() in all cases.

Fourth, the routine formerly called verbInf() has been renamed to getQuestionInf(), to better describe its purpose. This routine doesn't return the full infinitive form of the verb, but rather returns an infinitive form specifically for an interrogative in which one of the verb's objects is the interrogative's unknown. The old name was always misleading, but the addition of getInfPhrase() would have made the name even more confusing because of the similarity of the names.

In the routine that tries to free up space in an actor's hands by moving objects into a "bag of holding," objects with no encumbering bulk are now never moved. In the past, objects were moved in descending order of encumbering bulk, so objects with zero bulk were typically ignored; however, if there weren't enough bulky objects present, it was possible for the routine to try moving bulkless items. Since worn items have no encumbering bulk by default, this meant the routine sometimes removed worn items and moved them to the bag, which was pointless.
The new class FloorlessRoom, a subclass of Room, makes it easier to build rooms that represent locations without conventional floors. Anything dropped in the room is described as falling and vanishing below, since there's no floor for it to land on. You can control where dropped objects land using the bottomRoom property; by default, this is nil, which means that dropped objects simply disappear from the game.
The Room class no longer requires the room's floor to be represented by the first element of the roomParts list for the room. Instead, the new method roomFloor returns the room's floor object; this method finds an object of class Floor in the roomParts list and returns it, or nil if there no object of class Floor in the list. This change eliminates the fragility caused by the former convention.
A new mechanism provides more consistent handling when objects are dropped into a room. The new mechanism is used in the library to handle the DROP and THROW commands, both of which can result in an object being dropped into the enclosing room. The mechanism is extensible, so library extensions and games can add their own actions that drop objects into the room.

Whenever an object is to be dropped into the enclosing room, the DROP and THROW commands first find the drop destination using the existing getDropDestination() method. Then, they invoke the new receiveDrop(obj, desc) method on the drop destination object. In most cases, the drop location is the enclosing room, so the enclosing room's receiveDrop() method is usually the one that will be invoked.

The receiveDrop() method is responsible for carrying out the effects of dropping the object, including any game-state changes (such as moving the dropped to its new location) and reporting the results of the action.

In most cases, the effects won't vary according to which command was used to discard the object. This is the reason that there's only one receiveDrop() method, rather than separate methods for DROP, THROW, and any library extension or game-specific verbs. By using a common routine to handle all means of discarding an object, the game only has to implement any special handling once.

However, the message will almost always vary according to the command, for the simple reason that the message needs to acknowledge what the actor did to discard the item in addition to the effects of dropping it. This is the main reason the 'desc' argument is provided: it's a special "drop descriptor" object that lets you find out what command was used. You could also look at gAction to find the Action being performed, but the descriptor object makes your job a lot easier by providing some convenience methods for building report messages.

The descriptor object is of class DropType. This class has two important methods. First, standardReport() displays the "normal" report for the action. For DROP, this is simply "Dropped." For THROW, this is a message of the form "The ball hits the desk, and falls to the floor." You can use the standard report when your receiveDrop() method doesn't do anything out of the ordinary and thus doesn't require a special report. Second, getReportPrefix() returns a string that gives the start of a sentence describing the action. The returned string is a complete main clause for a sentence -- but it has no ending punctuation, so you can add more to the sentence if you like. The main clause will always be written so that the object being dropped will be the logical antecedent for any pronouns in subsequent text, which lets you tack on a string like this: ", and it falls to the floor."

Rather than building a message from the report prefix provided by the descriptor, you are free to build a completely custom message, if you prefer. To do this, you could use 'modify' to add your own method to each DropType subclass in the library, and then call this method from your receiveDrop(). Alternatively, you could use ofKind() to check what kind of DropType descriptor you got, and show custom messages for the ones you recognize.

Thing.stopThrowViaPath() now simply calls throwTargetHitWith() by default. When an object interrupts a projectile's course, the result is exactly as though the projectile had been successfully thrown at the interrupting object, so the separate implementations were redundant.
finishGame() now resets the sense context. This ensures that if the event that triggered the end of the game happened while a blocking sense context was in effect, the finish options are still listed. (Since the finish options involve direct interaction with the player, they obviously shouldn't be displayed in any NPC sense context.)
Thing now has a constructor, which calls initializeThing(). This ensures that any Thing objects that are created dyanmically (with 'new') are initialized in the same manner as objects defined statically.
The library's handling of RESTART has been changed slightly to correct a problem that cropped up under certain unusual circumstances. The old handling reset the VM within the Restart action execution method, and then threw a RestartSignal to tell the main loop to re-enter the game from the beginning. This approach had a subtle flaw: if any dynamically-created objects were referenced from an intermediate stack frame in the code that initiated the restart, those objects were retained through the VM reset because they were still accessible via the stack. These objects then had a chance to remain visible to an object loop, such as those that run during the library pre-initialization. The Restart action handler now simply throws the RestartSignal to the main loop, and the main loop now performs the VM reset. This ensures that the stack is reset to initial conditions before the VM reset occurs, which in turn ensures that only those dynamic objects that should survive the reset do survive the reset.
3.0.6j

Released August 2, 2003

The new class DistanceConnector is a specialized subclass of SenseConnector that allows two (or more) locations to be connected, but at a distance. This is handy for situations such as divided rooms (where we use two or more Room objects to model a single large physical location), and for cases where two rooms are open to one another (such as a balcony overlooking a courtyard).
The new class Vaporous is a subclass of Intangible designed for insubstantial but visible objects, such as fire, smoke, and fog. This class works a lot like Intangible, but it has a visual presence, and it specifically allows the commands Examine, Smell, and Listen To; in addition, it allows the commands Look In, Look Under, Look Behind, Look Through, and Search, and responds to these with "You just see (the object)."
The Lister class no longer sets the 'seen' attribute of objects it lists, because this attribute is now more generally handled in the main room description routines (per the change in meaning of 'seen' in 3.0.6i). As a result, the Lister.markAsSeen method has been removed.
A new macro, gSetKnown(obj), marks an object (usually a Thing or a Topic) as known by the player character. This is simply short-hand for obj.setKnownBy(gPlayerChar, true), for convenience.
The EventList object has been enhanced slightly to accept property pointer entries. A property pointer entry is interpreted as a property to invoke on the EventList itself, with no argument. This allows writing complex steps as separate properties of the EventList object, and then referring to them from the script by property pointer.

In addition, the ScriptEvent class has been eliminated. Instead, simply use Script objects in an event list. So, when an object appears in an event list, the EventList class now invokes the object's doScript() method. This makes it easy to build event lists recursively.

The new EventList subclasses CyclicEventList and StopEventList provide new pre-defined options for the behavior of an event list after visiting all elements. The new subclass SyncEventList provides a list that synchronizes its state with another list.

The TextList, StopTextList, and SyncTextList classes are now simple subclasses of CyclicEventList, StopEventList, and SyncEventList, respectively.

A new EventList method, scriptDone(), is invoked by doScript() after it processes the script's current step. By default, this method simply invokes advanceState() to advance the event list to its next state. Subclasses can override scriptDone() so that it does not advance the script's state, or does something else in addition.

The new EventList subclass ExternalEventList overrides scriptDone() so that the method does nothing. This makes it easy to create an event list that is driven externally; that is, the event list doesn't advance its state when doScript() is invoked, but only advances its state in response to some external processing that calls advanceState() directly on the event list object.

The Room class now provides a dobjFor(Examine) that replaces the Examine action with a Look action. This means that if a Room object has ordinary vocabulary words defined, and the player types EXAMINE (room), the command will automatically be treated as though the player had typed LOOK AROUND.
The new class IndirectLockable is a subclass of Lockable that can be used for situations where a lockable object cannot have its locked status directly manipulated by LOCK and UNLOCK commands. The reply to LOCK and UNLOCK can be customized via the object's cannotLockMsg and cannotUnlockMsg properties.
The Lockable class now handles the 'objUnlocked' precondition a little more subtly. The class now keeps track of whether or not the player knows the object to be locked; the status is assumed to be unknown initially. When the player doesn't know the status, the Open action does not apply an objUnlocked precondition, but instead disallows the action in the check() routine. When the status is known, the objUnlocked precondition is applied as before. Whenever the Open action fails in the check() due to the object being locked, the status is taken to be known for subsequent attempts.

This change makes the precondition handling a little more intelligent. When the player doesn't know the status beforehand, an Open command will simply fail with the discovery that the object is locked. This usually makes more sense than attempting to unlock the object automatically with an implied Unlock command, because if the object isn't already known to be locked, there's no reason to try to unlock it. When the status is known, however, it makes sense to perform the implied Unlock.

Objects whose lock status is visibly apparent can be marked as such with the lockStatusObvious property. This property is nil by default; an object whose lock status can be visibly observed should set this to true. When this property is set, the lock status is always known, since we'll assume that actors will simply look at the lock and observe the status. In addition, when this property is set, we'll add the locked/unlocked status at the end of the object's 'Examine' description (we'll add "It's currently locked" or "It's currently unlocked").

A new class, TravelWithMessage, can be mixed (using multiple inheritance) with TravelConnector or a subclass of TravelConnector to create a connector that shows a message as it's traversed. This is slightly more flexible than using TravelMessage, since TravelMessage can't usually be mixed with other TravelConnector-derived classes.
A new TravelConnector property, stagingLocation, gives the location in which a traveler must be located prior to traversing the connector. In the past, the connector simply assumed the traveler needed to be in the connector's direct location; this new property allows this condition to be customized. At times, the connector might be inside an intermediate container, for example, in which case the staging location might be a container of the container. By default, the staging location is the connector's location, so connectors that don't override stagingLocation will have the same behavior as before.
A new Thing method, roomLocation, returns the 'self' if the object is capable of serving as a location for actors, or the nearest container that is a roomLocation otherwise.

The Passage travel connector class now uses the other side's container's roomLocation as the default destination of the passage, rather than the other side's immediate container, as it did in the past. This makes it much easier to create passages that model non-trivial containment relationships, such as holes in walls, since an intermediate container (between the Passage and the enclosing Room or NestedRoom) will no longer create any confusion in performing the travel.

The NoTravelMessage class can now multiply inherit from Script, for convenience in defining message lists. If you want to provide several different messages to choose from when the connector is traversed, you can make your connector object inherit from TextList as well as NoTravelMessage, and the travelDesc() will automatically invoke the script to show a message.

If the travel connector has a non-nil location, the TravelConnector class's connectorTravelPreCond method adds a precondition that the connector must be touchable. This ensures that characters won't be allowed to traverse connectors that are visible but on the other side of a window, for example, or connectors that are out of reach.

The travel notification protocol has been changed very slightly. The travel connector's noteTraversal() method is now invoked from within the Traveler.travelerTravelTo() method rather than the TravelVia handler in the connector. This change means that noteTraversal() is called just before the traveler is actually moved, and in particular that it occurs after all of the other travel notifications (beforeTravel, actorTravel, and travelerLeaving). This is generally the more desirable ordering for the notifications, because it puts the connector's reaction closest to the actual traveler movement. If you want the connector to do something special before any of the other notifications, you can override the action() handler in the connector's dobjFor(TravelVia) so that it performs the special code and then inherits the base class handling.
When an actor travels through a connector that leads to a location with a posture other than the actor's original posture, the new location now sets the actor's posture directly, rather than via a nested action. For example, if a travel connector leads to a chair, then traversing the connector will move the actor into the chair and set the actor's posture to 'sitting', without activating a nested Sit action. In the past, the nested Sit action would have been used instead. This change is in BasicLocation.travelerArriving(), which sets the new actor's posture via a call to Actor.makePosture().

This change is generally desirable because we usually want arrival by travel connector to be self-contained; we don't want it to appear as though the character walked to the new location and only then performed a SIT ON THE CHAIR command (say). It's worth noting, however, that any side effects of the usual way of getting into the new location's posture will be skipped. Therefore, when linking a travel connector into a nested room with a non-standing posture, you'll need to consider any side effects you'd want to trigger, and trigger them explicitly on the travel connector itself.

The new Actor method scriptedTravelTo() simplifies coding of scripted travel for an actor. This routine is suitable for cases where an NPC is to perform a series of scripted travel actions, where each room along the way is scripted in advance. This routine is not suitable for goal-seeking NPC's, because it can only perform travel to a location adjacent to the actor's current location, and because it's "omniscient" (it doesn't take into account the actor's knowledge of the the map, but simply considers the actual map). For cases where an NPC is to visit a scripted series of locations, this is a convenient way to accomplish the travel, since it requires specifying only the destination of each step of the travel.
The class SecretDoor is now based on BasicOpenable as well as ThroughPassage. The class previously didn't have BasicOpenable anywhere among its superclasses, which prevented its open/closed mechanisms (initiallyOpen, isOpen, makeOpen) from working consistently with other types of doors, which are all based (indirectly) on BasicOpenable.
The new ThroughPassage subclass PathPassage is a specialization for cases such as outdoor walkways, paths, and streets. The key difference between the standard ThroughPassage and the more specialized PathPassage is that path passages are not considered enclosed, so the descriptions for arrival and departure via a path take this into account. In the English messages, we describe travel on a path as being "via" the path rather than "through" it. In addition, the English parser accepts "take" as a synonym for "enter" or "go through."

To handle this new class, TravelMessageHandler and its subclasses have a new pair of methods, sayArrivingViaPath() and sayDepartingViaPath(), for generating travel messages related to paths.

The new method Actor.actorTravel(traveler, connector) complements beforeTravel() and afterTravel(). This new method is called on the actor who initiated the travel (that is, the actor performing the command, if any); it's called after the beforeTravel() notifications are sent to nearby objects. This method is provided for symmetry with the beforeAction/actorAction/afterAction set.
The AccompanyingState.accompanyTravel() method has been changed slightly. The first parameter is now a Traveler object, which might be an Actor but could also be another kind of Traveler, such as a Vehicle. This allows the game to determine whether the NPC is to accompany actors when they travel on vehicles or in other indirect ways.

In addition, the class's beforeAction() method, which responded to the initiating actor's travel, has been changed to a beforeTravel() method. This takes advantage of the more precise kind of notification offered by beforeTravel(). In particular, using this method avoids unnecessary attempts to accompany an actor who attempts a travel command that doesn't actually result in travel (because the connector doesn't go anywhere, for example).

If a travel connector is located inside a nested room, the preconditions for traversing the connector now correctly move the character to the nested room prior to the travel. In the previous library version, the enclosing room tried to add its own precondition requiring the traveler to be in the outer room, which was incorrect.
The Follow command now provides better feedback to the player in certain cases. When the player has previously observed the target actor departing, but from a different location than the player's current location, the Follow command will now respond with the message "You didn't see which we he went" (rather than "You can't do that from here" as it did in the past).
An Actor's executeTurn() method, and all of the methods it invokes (the Actor's idleTurn() and the ActorState's takeTurn(), for example), now run within a valid "action environment," just like fuses and daemons started doing in 3.0.6i. In addition, the executeTurn() method sets up a visual sense context for the actor, ensuring that any output displayed in the course of the actor's turn is displayed only when the actor is visible to the player character. (It is presumed that the effects of any action that an actor performs are visible if and only if the actor is visible. This is just the default case, though: the game is always free to set up a separate sense context, using callWithSenseContext(), whenever necessary.)

To facilitate this change and make it easier to override an actor's per-turn processing, the main handling for an actor's turn is now in a new method, executeActorTurn(). The executeTurn() method simply sets up the action environment and then invokes executeActorTurn(). In most cases, subclasses that need to specialize the per-turn processing should override executeActorTurn() rather than executeTurn(), since this will let the override run in the action environment.

The optional initial "look around" that the runGame() function performs is now executed within an "action environment," just like fuses and daemons started doing in 3.0.6i. This allows the initial room description to refer to gActor and gAction, which some library methods do implicitly.
callWithSenseContext() now uses "lazy evaluation": rather than immediately calculating the effect of the new sense context, the routine now simply notes that it has a new context, and defers calculating the effects of the new context until the effects actually need to be known. This makes it very inexpensive to set up a new sense context in cases where the context won't actually be needed for anything, which is frequently the case in daemons, fuses, and the standard per-turn processing for NPC's. This can save a substantial amount of time when a game has numerous background events if, as is typical, most of the background events don't generate any output on a given turn.
The new macros actorStateDobjFor(action) and actorStateIobjFor(action) make it convenient to delegate an actor's processing for an action to the actor's current ActorState object. These are defined in adv3.h, and work analogously to asDobjFor(action) and asIobjFor(action). Using these macros involves two steps. First, in the Actor, write a short handler for the action that uses the macro to delegate to the state object:

  bob: Person
    // ... other definitions ...
    dobjFor(AttackWith) actorStateDobjFor(AttackWith)
  ;

This sets up the "bob" Actor object so that the handling for AttackWith, with bob as the direct object, will be delegated to bob's current ActorState object. Second, just write a normal dobjFor() handler in each of the ActorState objects associated with bob:

  + bobFighting: ActorState
    dobjFor(AttackWith)
    {
      action() { "Bob puts up a fight..."; }
    }
  ;
  + bobCowering: ActorState
    dobjFor(AttackWith)
    {
      action() { "Bob just hides in the corner..."; }
    }
  ;
The withActionEnv() function now takes an addition parameter giving the Actor object to use as the gActor value while running the callback. In the past, this function always used the player character Actor (given by gPlayerChar); the new parameter allows other actors to be used instead.
The withCommandTranscript() function now returns the result of the callback function, rather than the transcript object created to run the callback. Callers who still need the transcript object as the result can simply return gTranscript from the callback function itself, as this value will then be passed up as the return value from withCommandTranscript().
The attentionSpan property of an InConversationState object can now be set to nil to indicate that there is no attention span limit for the NPC. Setting attentionSpan to nil will prevent the NPC from ever ending the conversation by virtue of being ignored for too long.
The standard SpecialTopic template (in adv3.h) now allows defining a list of strings (for the textStrings property) in place of a single response. The standard TopicEntry, SpecialTopic, and YesNoTopic templates now make the response/response list entry optional, allowing the rest of the templates to be used even if a custom method is needed to handle the response.
A new DefaultTopic subclass, DefaultAnyTopic, matches all of the different sorts of topic interactions: ASK, TELL, GIVE, and SHOW. This is especially useful for default handling in conversation nodes (ConvNodes) where you want catch all topics not otherwise handled.
The new TopicEntry property isConversational lets a TopicEntry specify whether or not the response is "conversational." A conversational response is one that involves some kind of interaction with the NPC. This property is true by default, but some response objects will want to set it to nil to indicate that the response doesn't involve any conversation with the NPC. For example, a response like "You don't think he wants to discuss that right now" is non-conversational, because it doesn't involve any exchange with the NPC.

When isConversational is nil for the response object that's found for a conversation command, a ConversationReadyState will not enter its in-conversation, but will simply show the response and remain in the conversation-ready state. This means that there will be no "greeting" exchange for these responses.

The new ConvNode method canEndConversation(actor, reason) lets a node prevent a conversation from ending. 'reason' is an enum indicating what is triggering the attempted termination: endConvBye if the player typed BYE; endConvTravel if the other actor is leaving (i.e., the player typed a travel command); or endConvBoredom if we're terminating the conversation on our own because our attentionSpan has been exceeded. This method can cancel the operation that attempted the termination simply by returning nil; the method should display an explanation when returning nil, to let the player know why the command is being canceled.

In related changes, ConvNode.endConversation() and ActorState.endConversation() now take 'reason' codes rather than 'explicit' flags. This gives the methods full details on why the termination is occurring.

The new AgendaItem class provides a simple new mechanism for handling cases where you want to model an NPC in terms of motivation. Each actor has an "agenda," which is a list of AgendaItem objects. On each turn, the actor's ActorState object (in the takeTurn method) will look at the agenda list, to see if any items are ready to execute. If there's an item that's ready to execute, the ActorState will execute the first one that's ready.

Each AgendaItem object has a method called isReady, which indicates whether or not the item is ready to execute. An actor will only execute an agenda item once it's ready. The invokeItem method contains the code that actually carries out the agenda item's action.

By default, an agenda item is removed from the actor's agenda list when it's executed. However, if the item's stayInList property is true, then the item will not be removed on execution. This property lets you handle agenda items which the actor will try repeatedly, until some other code determines that the goal has been achieved and removes the item from the actor's agenda.

You must nest AgendaItem objects within their actor, using the "+" notation. This associates the AgendaItem objects with the actor, but it doesn't add them to the actor's agenda list. You must explicitly add agenda items to the actor's agenda list by calling the actor's addToAgenda() method. Agenda items must be explicitly added because an actor's motivation typically will change dynamically as the game's events unfold.

Autonomous NPC conversation continuation processing has been moved from InConversationState to the base ActorState. This allows actors that don't use greeting protocols to nonetheless use stateful conversations.

To accomodate this change, the default takeTurn() method in ActorState now preforms more processing. If the actor has an active ConvNode, then this takes precedence over any other default processing for the actor's takeTurn() method. If the actor hasn't engaged in conversation on the same turn, we invoke the ConvNode to continue the conversation under NPC control; otherwise, we do nothing more. If there's no ConvNode, we process the actor's "agenda." If there's no ConvNode, and the actor has no agenda items that are ready to execute, and the ActorState inherits from Script, then we invoke the script. This structure means that a stateful conversation takes precedence over everything, and an agenda item takes precedence over state-level background activity.

In the Actor class, the TellAbout and AskAbout handlers now include the "canTalkToObj" precondition by default. This ensures that the NPC being addressed can actually hear the other actor, to disallow conversations in cases such as when the two actors are too far apart to hear one another. The HELLO, GOODBYE, YES, and NO commands now all make the same check as well, in the sayToActor() method.

Similarly, ConversationReadyState now makes the same check before going through a greeting. If the initating actor can't talk to the target actor, the enterConversation() method says so and uses 'exit' to terminate the command. This prevents strange situations from arising commands that don't require the canTalkToObj precondition. (SHOW TO works this way, because the actors don't necessarily have to be able to talk to each other to use SHOW TO; we could show a note to an NPC on the other side of a sound-proof window, for example.)

The new TopicEntry subclass InitiateTopic provides an easy way of making an NPC initiate conversation based on simulation objects in the environment. To make an NPC trigger a conversation, call the actor's initiateTopic(obj) method, where 'obj' is the simulation object you want to use to key the conversation. This will find an InitiateTopic object in the actor's topic database matching the given object ('obj'), and show its response text. One easy way to use this is to initiate conversation based on the NPC's current location: just create InitiateTopic objects keyed to those locations where you want the NPC to say something special, and add a line like this to the ActorState's takeTurn() method:

  getActor().initiateTopic(getActor().location);
The "topic inventory" list shown by the TOPICS command is no longer enclosed in parentheses by default. The parentheses are still included when the topic inventory is shown implicitly (by a TALK TO command or a <.topics> tag, for example).
The new ConvNode property limitSuggestions allows a ConvNode to indicate that suggested topics from the broader conversation context should not be included in a topic inventory listing. This is useful for times when the ConvNode won't allow the player to stray from the subject, such a a ConvNode that only allows a YES or NO answer to a question. In these cases, set limitSuggestions to true. The property is nil by default, which causes topic inventory listings to include not only any suggestions defined within the ConvNode, but also any defined in the active ActorState or in the Actor itself.
The new Actor.scheduleInitiateConversation(state, node, turns) method lets the game set up an NPC to start a converation at the next opportunity after the given number of turns has elapsed. If 'turns' is zero, then the conversation can start on the next turn on which the NPC hasn't been targeted for conversation by the player; if 'turns' is one, the player will get at least one more turn before the conversation can start, and so on for higher 'turns' values. The important thing is that the conversation can only start on a turn on which the NPC isn't targeted with a conversational command (ASK, TELL, etc). The Actor checks its list of pending conversations on each turn in its takeTurn() method, before invoking the current state's takeTurn() method.
In the previous version, if Actor.initiateConversation() was called from within the player character's turn (in an NPC's beforeAction() handler responding to a player character action, for example), and the ConvNode had a "continuation" message (defined in npcContinuationMsg or npcContinuationList), the first continuation message was incorrectly shown on the same turn. This no longer occurs; the continuation message won't be shown until the next turn at the earliest. ("At the earliest," because a continuation message won't be shown on a given turn if the player character addresses a conversational command to the NPC on that turn.)
When an actor continues a conversation of its own volition (using npcContinueMsg or npcContinueList) in a ConvNode, if the continuation routine actually displays any text, then the ConvNode will automatically set the player character and the initiating NPC to be in conversation with one another. This ensures that any subsequent conversational command from the player will have the initiating NPC as the default interlocutor.
In the past, the REPLAY QUIET command didn't display any acknowledgment, and improperly left all subsequent text in bold-face. This has been corrected.
ActorState.greetingsFrom now delegates to a method of the same name in Actor; this makes it easier to change the default greeting for a simple actor without a state object.

Along the same lines, ActorState.showGreetingMsg has been removed, and replaced with Actor.defaultGreetingResponse. The method has been moved to Actor because of the change to greetingsFrom, and the method has been renamed for consistency with the similar pattern for the other conversation command default message methods (for ASK, TELL, etc) in Actor.

Along the same lines, ActorState.goodbyeFrom now delegates to a method of the same name in Actor by default. A new Actor method, defaultGoodbyeResponse, displays the default message, for consistency with the naming of the similar methods for other conversation commands.

Two new ConversationReadyState methods have been added: enterFromByeMsg and enterFromConvMsg. These are provided for convenience as single-message complements for enterFromByeList and enterFromConvList, respectively. By default, these simply invoke their respective list scripts.
VocabObject has a new feature that allows an object's vocabulary to be divided into "strong" and "weak" tokens. Weak and strong tokens are the same as far as the parser is concerned, and are entered into the dictionary as usual. The difference is that a weak token can only be used to refer to an object in combination with one or more strong tokens. That is, if the player enters a noun phrase, and the noun phrase matches all of the vocabulary for a given in-scope object, the object will match the noun phrase only if one or more of the words in the noun phrase is a strong token for the object.

The purpose of this new feature is to make it easy to define extra vocabulary for an object that the parser accepts for an object without creating any new ambiguity. This comes up in situations where you simply want to create additional synonyms for an object, as well as in cases where an object is most strongly identified in terms of its relationship to something else. For example, you might have a location that contains a couple of doors, one to a house and the other to a shed. You could use adjectives ("house door" and "shed door") to differentiate the doors, but it might be more natural to use a prepositional phrasing, such as "the door of the house" and "the door of the shed." If you also have a house and a shed object, though, this phrasing would create unwanted ambiguity with those objects. This is where weak tokens come in. If you define "house" and "shed" as weak tokens for the respective door objects, the parser will never take those words alone to refer to the doors, so there will be no ambiguity with the house and shed objects themselves; but the parser will still allow "house" and "shed" to refer to the doors, as long as they're used in combination with the strong token "door".

Weak tokens are defined per-object (they're not global). Each object that has weak tokens keeps a list of its weak tokens in its 'weakTokens' property. In the basic VocabObject.matchName() implementation, the object checks the noun phrase to see if it consists entirely of weak tokens, and rejects the match if so.

When you define an object, you can define weak tokens in one of two ways. If you define 'noun' and 'adjective' (etc.) properties directly, simply define a 'weakTokens' property containing a list of strings giving the object's weak tokens. If you use the vocabulary initialization string, simply enclose each weak token in parentheses. For example:

+ Door 'front door/(house)' 'front door of house' 
  "The door is badly weathered. "
;
VocabObject.matchName() and matchNameDisambig() now invoke a new method, matchNameCommon(), to carry out their common handling. This change facilitates the weak-token feature mentioned above. In most cases, when a game needs to override an object's name matching test, it should override matchNameCommon(), since this method provides the common handling for normal matching and disambiguation matching. Games can still override matchName() and/or matchNameDisambig() individually, but it's only necessary to do so when you want to use different rules for normal matching and disambiguation matching.
The English parser's handling of quoted string literals as adjectives has been improved slightly, but the changes will require some small adjustments to any games currently using literal adjectives.

Instead of allowing any adjective to be quoted, the parser now only matches quoted strings that are specifically designated as "literal adjectives" in an object's vocabulary. To designate an adjective as a literal adjective, you can either enclose the adjective in double-quotes in the vocabulary initializer string, or you can explicitly define the word using the 'literalAdjective' part-of-speech property instead of the normal 'adjective' part-of-speech. For example, you could define an elevator button for the lobby level like so:

+ Button '"L" button' 'L button' "It's a button labeled <q>L.</q> ";

This change makes the parser stricter about which words it accepts as quoted adjectives, but it also allows the parser to be more flexible about requiring the quotes. In particular, the parser now accepts all of the following forms for the button defined above: "L" button, button "L", L button, and button L. In the past, the parser was unable to accept the last of these, because without the quotes, it wasn't able to tell that the unquoted "L" was meant as a literal adjective. Now that the "L" is defined in the dictionary as a literal adjective, it's no longer necessary for the player to quote the word for the parser to recognize it as a literal adjective, so the parser is able to accept the form with the literal adjective following the noun.

Note that this change also involves a subtle change to the special "*" vocabulary wildcard token. In the past, if the string '"*"' (that is, an asterisk enclosed in double-quotes) appeared as an 'adjective' dictionary entry for an object, then that object matched any quoted string used as an adjective in player input. Now, this effect is obtained by using the string '*' as a 'literalAdjective' dictionary entry for an object. This change has no effect at all on the way you write vocabulary initializer strings, since you still enclose the asterisk in quotes in that string; however, if you're defining a 'literalAdjective' list directly for an object, you now must drop the quotes and add a word entry consisting of simply the asterisk.

The English parser now accepts pronouns as responses to disambiguation questions. (The disambigListItem(noun) rule now matches completeNounPhraseWithoutAll rather than qualifiedNounPhrase, as it did in the past; the new sub-production matches pronouns, which qualifiedNounPhrase does not.)
In the English parser rules, the command "T topic" is now accepted as a short-cut for "TELL ABOUT topic." Together with the "A topic" shortcut for ASK ABOUT, this can greatly reduce the amount of typing a player has to do in a game that has a lot of ASK/TELL character interaction.
In the English parser rules, the Hello action now accepts "hallo" as equivalent to "hello" or "hi", and also accepts "say" with any of these variations.
A new Action method, callAfterActionMain(obj), allows a game to register an object for invocation at the end of the current action. This is only meanginful when used on the current gAction object, since it registers for notification of completion of the current action. When the current action is finished - including the iteration over all of the objects involved in the action - the action invokes the afterActionMain() method of each registered object.

This new method is especially useful for adding a "summary" of an action that involves multiple objects. Each individual object's action handler (an action() method in a dobjFor() block, for example) can register a handler object to receive notification when the overall command is finished. A given object can be registered only once - redundant registrations are simply ignored - so each individual iterated object doesn't have to worry about whether or not other iterated objects will register the same handler. Then, at the end of the action, the handler will be invoked; it can determine what happened in the action, and do something based on the end result. For example, the handler could scan the transcript (gTranscript) for certain types of reports, and add a summary message based on the reports, or could even replace some of the individual reports with a summary. The handler could also take addition action based on the overall end results; for example, in a GIVE TO command, a handler could look at the full set of objects successfully given, and decide that the combination is sufficient to allow the recipient to give the donor something in return.

A new CommandTranscript method, summarizeAction(), can be used with the new callAfterActionMain() system to generate a summary of an action that involves multiple objects. summarizeAction() scans through the transcript, looking for runs of two or more reports for the current action that match criteria specified by the caller (via a callback function). For each run of two or more consecutive qualifying reports, the method removes those reports, along with their corresponding multi-object announcements, and replaces the last of the reports with a single "summary" report generated by the caller (via another callback function). This method lets you turn a transcript like this:

   gold coin: Bob accepts the gold coin.
   gold coin: Bob accepts the gold coin.
   gold coin: Bob accepts the gold coin.

into something like this:

   Bob accepts the three gold coins.

This sort of summary isn't straightforward to generate in general, and the library makes no attempt to apply it generically. In specific cases where you can control the range of possible results, though, this can be a powerful way to improve the game's output by describing an iterated action as though it were a single logical unit.

Phrases involving quantities ("five coins") and "all" with a plural ("all coins") are now resolved a little more consistently and intuitively, from a player's perspective. First, phrases involving quantities are now always disambiguated in the same manner as singular phrases, and then the required number of objects is chosen; in the past, disambiguation filtering was not as consistent. This change ensures in particular that "collectives" are properly filtered in or out; the old mechanism sometimes missed collectives because it sometimes skipped the normal disambiguation filtering. Second, "all"-plus-plural phrases are now resolved to all matching objects, rather than to only the most logical subset of matching objects, as was the case in the past. This means that a command applied to "all coins" really is applied to all coins that are present, even those for which the action isn't currently valid.

To implement these changes, NounPhraseProd has a new method, getVerifyKeepers(), that the disambiguation filtering methods call to reduce the list. The default NounPhraseProd definition of the method does the traditional filtering that the disambiguation filter did, which keeps just the most logical subset of the results. AllPluralProd overrides this method to keep everything in the list, and QuantifiedPluralProd overrides the method to select a subset with the desired number of entries.

The CollectiveGroup object by default no longer matches a noun phrase that specifies a quantity ("take five coins" or "take both coins"; or any singular phrase, since a singular phrase specifies a quantity of one). Collective groups are designed to stand in for a completely collection of in-scope individuals, not for arbitrary subsets of the individuals, so when a quantity is specified we must fall back on iterating over a selected subset of the individuals.

To implement this change, the filterResolveList() method now takes two additional parameters: one giving the quantity specified in the noun phrase, if any, and another giving the role played by the object (DirectObject, IndirectObject, etc). When the noun phrase specifies a quantity, the new parameter will be set to an integer giving the quantity specified. If the noun phrase is unspecific about the quantity (as in "take coins" or "get all coins"), then the quantity parameter will be nil.

Note that the isCollectiveAction() method now takes an additional parameter as well, giving the role in the action played by the object being resolved (DirectObject, IndirectObject, etc). This allows differentiating the handling based on both the action and the role played in the action.

Custom CollectiveGroup objects can represent specific quantities of objects, from the player's perspective, if desired. For example, a game might want to create a CollectiveGroup that represents a quantity of money, rather than dealing with the individual coins making up the quantity. To do this, the CollectiveGroup object must override the filterResolveList() method, and must set the "quant_" element of its own ResolveInfo entry to an integer giving the number of grammatical objects it represents. The CollectiveGroup must keep track of what it's representing somehow; the best way to do this is to create a new instance of the CollectiveGroup itself, and store a property in the new instance giving the quantity represented by the collective. Note that using CollectiveGroup objects in this manner is tricky; you'll need to code action handlers for the custom CollectiveGroup object so that they correctly take into the account the quantity represented by the group object.

The ownershipAndLocationDistinguisher has been refactored into two separate distinguishers: ownershipDistinguisher and locationDistinguisher. Each of these new distinguishers is very similar to the old combined distinguisher, but the ownershipDistinguisher gives priority to ownership, regardless of the containment relationship. The library classes that define distinguishers now use both of these, with the ownership distinguisher applied first.

The purpose of this change is to ensure that ownership will be used as a distinguishing feature whenever possible, before the parser falls back on location. The old combined distinguisher could only use ownership in the limited case where the owner and immediate location were the same, because it couldn't otherwise be sure that it would be able to distinguish multiple objects with the same owner but different locations. By separating the ownership and location distinguishers, we first try to identify objects purely by ownership; when this fails, we fall back on the old approach of identifying by immediate location in preference to owner.

To support this change, the English methods for owner-or-location names (aNameOwnerLoc, theNameOwnerLoc, countNameOwnerLoc) now take a parameter indicating whether to use ownership or location as the first priority in generating the name. When ownership takes priority, these methods will show the name with a possessive form of the owner regardless of the containment relationship between the object and its owner.

Possessive qualifiers (such as "bob's" in "bob's chair") are now resolved in a special resolution context that allows referring to owners that aren't in scope for the purposes of the phrase being qualified (such as the "chair" in "bob's chair"). A possessive phrase is now considered in scope if it's in scope for the phrase being qualified or it's known to the actor performing the command. This allows possessive phrases to be used to refer to objects that are present, and which are known to belong to an actor, even if the actor itself isn't present.
When the parser generates a disambiguation prompt, and the prompt distinguishes objects by possessives ("which match: bob's match or your match?"), the parser automatically sets the pronoun antecedents for "him" and/or "her" to the people mentioned in the prompt. This allows the player to answer with "his" or "hers" (or "his match"), referring back to the person or people mentioned in the message.
The parser error messages that display literal text entered by the player (such as the message for unrecognized punctuation marks, and the message for unknown words) now HTML-ify the user's text. This ensures that any markup-significant characters (such as "<" and "&") are converted into HTML sequences that display the corresponding characters.
A bug in the English parser's rule for phrases like "anything in the box" caused run-time errors when the word "anything" alone was used as a noun phrase. This has been fixed.
In the hint system, the new Goal methods openWhenTrue and closeWhenTrue can be used to define arbitrary conditions that open and close the goal. These new general-purpose conditions supplement the more specific conditions (openWhenSeen, openWhenAchieved, etc.); the goal will be opened if any of the openWhenXxx conditions are met, and will be closed when any of the closeWhenXxx conditions are met.

The new Goal property openWhenDescribed lets you specify that the goal should be opened when the referenced object is described (usually with EXAMINE). This is good for goals where the existence of a puzzle isn't made apparent until the full description of an object is viewed. The new property closeWhenDescribed correspondingly closes the goal when the referenced object is described.

The RESTART command no longer resets the commandSequencer to the "no-command" state, as it did in the past. This change corresponds to the change in commandSequencer start-up state effected in 3.0.6i.
The English grammar now accepts '1' as a synonym for 'one' or 'any' in singular indefinite noun phrases, such as "take 1 coin." (In the past, the grammar omitted this alternative.)
The new class SecretFixture is designed for objects that are needed in the internal implementation but are not meant to be manipulated directly by characters. This is a simple subclass of Fixture; the main difference is that a SecretFixture is hidden from "all," to help prevent direct references to it in commands.
In the English grammar, a new production named singleNounOnly can be used where a single noun (rather than a list of nouns) is structurally required. The singleNoun production itself will structurally match a list of nouns, but considers such matches semantically invalid. In contrast, singleNounOnly won't even structurally match a noun list.

The firstCommandPhrase(withActor) rule now uses the new singleNounOnly production to match the actor phrase. This eliminates the structural ambiguity of certain types of invalid input that were able to cause unbounded memory consumption with the old rule.

The menu system no longer shows a border in full-screen menu windows. (When a menu takes up the whole game window, a border is superfluous, since there's no other window requiring separation. Removing the border slightly but noticeably improves the appearance of a full-screen menu.)
The Decoration class now gives Examine commands a reduced "logical rank" (of 70) for disambiguation purposes. This means that if the player enters an Examine command, and the vocabulary for the direct object matches a Decoration object and a non-Decoration object, the non-Decoration object will be chosen ahead of the Decoration. It's usually desirable to treat Decoration objects as second-class citizens for disambiguation purposes, because they're usually meant to stay in the background as much as possible.
The standard Actor handlers for the Give and Show actions now produce more sensible messages for giving or showing something to oneself. (The old message was the generic "you do not respond"; the new message is "Giving [or showing] that to yourself won't accomplish anything.")
The base definition of the method filterResolveList() has been moved from Thing to VocabObject. The parser calls this method on objects that match vocabulary in player input, so it more properly pertains to VocabObject than to Thing. (In practice, this is important in some cases when Topic objects match player input, because Topic objects descend from VocabObject but not from Thing.)
The cached scope list in Resolver and its subclasses is now better encapsulated, to make it easier to subclass Resolver. In particular, the "scope_" member is referenced only in the method cacheScopeList(), objInScope(), and the new method getScopeList(). This means that a subclass can dispense entirely with the cached scope list (and the "scope_" member), as long as the subclass implements objInScope() and getScopeList() to return mutually consistent results. A subclass can alternatively override cacheScopeList() to use a different set of rules to obtain and cache the scope list.

Along similar lines, TAction.initResolver() now calls cacheScopeList() to initialize its cached scope list, rather than doing so directly. This allows TAction subclasses to customize the scope rules in exactly the same way that subclasses of Resolver can customize scope. Likewise, TopicResolver.filterAmbiguousNounPhrase() now calls objInScope() rather than accessing the "scope_" list directly.

The new Achievement method addToScoreOnce() makes it a little easier to do score book-keeping in the common case of achievements that are scorable only once. This method adds the achievement to the score, with a given number of points, but only if the achievement has never been scored before; if the achievement has been scored before, the method does nothing at all. This makes it unnecessary to keep track of any separate status to avoid scoring the same action more than once. (Some achievements are meant to be repeatable; this method wouldn't be useful in such cases, obviously.)
The complexMultiTransform report transformer is now a little more liberal in determining when to add visual separation between reports when an action is iterated over multiple objects. In the past, visual separation (i.e., a paragraph break) was added only when implicit command announcements were present. Now, visual separation will be added any time the report group for an individual object in the iteration consists of more than one report, or a report has a single message with text over 60 characters in length. This new, more liberal policy is designed to add visual separation essentially any time an individual object's result message is non-trivial, since multi-object messages become hard to read when they're all jammed together, unless each individual message is very short. The new policy errs on the side of adding too much visual separation, which on balance seems to do considerably less harm to readability than too little separation.
The handling of OOPS responses has been improved slightly. In particular, when the player uses an OOPS command to correct an error, the new text that results from applying the OOPS substitution is now run through the normal pre-parsing sequence.

In the past, an OOPS command itself was run through the normal pre-parsing steps, but the new command resulting from applying the OOPS replacement text was not pre-parsed. This resulted in incorrect behavior in certain certain rare cases. One noticeable example was when a SpecialTopic was active. Because SpecialTopics rely on pre-parsing to match their special command templates, and because the corrected text after the OOPS wasn't pre-parsed, it was effectively impossible to use OOPS to correct a typo in a command intended to match a SpecialTopic. The new OOPS handling ensures that pre-parsing is run as normal on the new command resulting from applying an OOPS correction.

When the player enters a command that matches an active SpecialTopic, the library now treats this as a conversational action directed to the interlocutor, as though the player had typed an ASK or TELL command. This is important in cases where the actor has background actions that it performs based on the absence of an explicit conversational interaction from the player on any given turn, such as showing ConvNode continuation messages.
ASK FOR commands are now handled uniformly with other conversational commands (ASK ABOUT, TELL ABOUT, GIVE TO, etc). In particular, these commands are now routed to the ActorState for handling, and can be handled using the new AskForTopic class, which works along the same lines as GiveToTopic and ShowToTopic.
The library now automatically sets the antecedent for the appropriate pronouns to refer to a responding actor, any time a TopicEntry is used to generate response text. This ensures that the player can use a pronoun on the next command to refer to the last actor who generated a conversational message.
The new method Actor.setPronounObj(obj) makes it easier to set a simulation object as the pronoun antecedent for commands targeting the given actor. (In the past, only the setPronoun() method was available, which took a list of ResolveInfo objects. When the game or the library needs to set the pronoun directly, it's often easier to set it directly in terms of simulation objects, without creating a ResolveInfo list.)
The new classes SimpleNoise and SimpleOdor make it easier to define Noise and Odor objects for the common case that a noise/odor is just a background part of the room description that (1) doesn't require ongoing "daemon" announcements, and (2) doesn't need any differentiation among the different types of descriptive messages. These classes simply use the "desc" property as the default for all of the descriptive messages, and are marked as "ambient," to avoid automatic inclusion in the room description.
The Throw command now works properly when throwing something at oneself. (In the past, throwing something at oneself incorrectly tried to treat the actor as the drop location of the throw; it now correctly treats the actor's location as the drop location.)
The new NOTE verb accepts an arbitrary literal as its object; the command doesn't do anything except acknowledge the input. The purpose of the command is to allow the player to enter arbitrary notes into the session transcript as she plays the game. Players might want to make such notes for their own later reference, but this command is especially useful for play-testing, because it gives play-testers a very easy way of pointing out bugs or making comments directly in the session transcript where they apply, and at the moment they occur to the player.
A library bug caused a run-time error if an attempt was made to remap from a direct object handler (a dobjFor) to an intransitive action (an action with no objects). Remapping to intransitive actions will now work properly.
The RealTimeDaemon class had a couple of problems that prevented it from working properly. For one thing, it wasn't based on RealTimeEvent as it should have been; for another, it didn't reset its next firing time when fired. These problems have been corrected.
3.0.6i

Released June 15, 2003

The commandSequencer now starts in "before-command" mode, rather than in "no-command" mode. This allows any introductory text displayed before the first command to use the normal command sequencing tags.
The "seen" property of Thing has been changed slightly in meaning. In the past, this property was set to true only when an object was specifically listed as a portable item in a room description. Now, the property is set to true whenever a room description is displayed from the player character's perspective, and the object is visible to the player character. The important difference is that unlisted items, such as 'Fixture' and 'Heavy' items, or items with special descriptions, will be marked as seen as soon as their containing rooms are described.
Due to a bug in the previous version, the REPLAY command only worked when one of its qualifiers (REPLAY QUIET or REPLAY NONSTOP) was used; just plain REPLAY didn't work. The plain REPLAY command now works correctly.
The gameinfo.t module has been removed from the base system library. In its place, the GameID class has been extended to write the GameInfo data file automatically during pre-initialization. If the game defines a GameID object, then the library automatically writes a gameinfo.txt file, using values from the GameID object. Refer to the comments in modid.t for the GameID class.
Actor.setConvNode() now accepts a string giving the name of a ConvNode, as an alternative to a reference to a ConvNode object. In the past, only the object reference was accepted. Passing in a string naming a ConvNode now has the same effect as passing a reference to the ConvNode itself.
The new Actor method initiateConversation() makes it easier to code an NPC initiating a conversation. This method takes an ActorState object to use as the NPC's new state, and a ConvNode (which can be specified by the string name of the ConvNode) to use as the initial conversation node.

In addition, the new ConvNode method npcGreetingMsg is defined to display the initial conversational exchange in an NPC-initiated conversation. Any ConvNode used in an initiateConversation() call must either override npcGreetingMsg to define a greeting message, or define an npcGreetingList property as a TextList containing a list of greeting messages.

The new ConvNode method npcContinueMsg is defined to display a conversational "continuation" message from the actor. This method lets the NPC continue a conversation of its own volition if the player character doesn't do so. This method is invoked on each turn when the ConvNode is active, and the player didn't enter a conversational command on that turn. The method is invoked during the actor's takeTurn() daemon processing. To define a continuation message, you can either define npcContinueMsg as a double-quoted string with the message, or you can define npcContinueList to a TextList subclass containing a list of continuation messages.

If you don't override npcContinueMsg or npcContinueList, there will be no NPC-initiated continuation message. If you do provide a continuation message, then the ConvNode stays active by default. You can use the <.convnode> tag within the continuation message's text to switch to a new ConvNode, just as in a topic response message.

The commandSequencer now starts in "before-command" mode, rather than in "no-command" mode. This allows any introductory text displayed before the first command to use the normal command sequencing tags.
The Yes and No topic classes (YesTopic, NoTopic, YesNoTopic) had a number of bugs that prevented them from working properly. These classes should now work correctly.
A new template for DefaultTopic makes it easier to define these objects. The DefaultTopic template accepts simply a response string (double-quoted), or a list of response strings (single-quoted).
A bug in the AltTopic mechanism prevented intermediate AltTopics in nestings more than one level deep from being properly selected. This is now fixed.
In the English-language module, the miscWord production now accepts '#'-prefaced numbers and quoted strings. This provides more flexibility for inputs in things like topic phrases.
The AccompanyingState class didn't use the correct test for a travel action in its beforeAction() method, which prevented the class from carrying out its accompanying travel role at all. This has been corrected.
ActorState.obeyCommand() generated the wrong default message. This is now fixed.
ConvNode had a couple of problems that caused run-time errors or other strange behavior when activating a ConvNode that had no associated SpecialTopics. These have been fixed.
Fuses and daemons (including real-time events and prompt daemons) now run automatically in a standard Action environment. This means that you can call essentially any library code without having to worry about whether or not it will need to access gAction or gTranscript. In the past, fuse and daemon code was called with no Action environment in effect, which made it necessary to either limit the library calls made within the fuse or daemon, or to explicitly set up an action environment (using withActionEnv(), for example) before invoking library code. This added a lot of unnecessary complexity to fuse/daemon code. Now that these routines are always invoked with a valid Action environment, these restrictions have been lifted.
The withActionEnv() method now takes an additional argument, giving the specific Action class to use for the dummy action object in effect during the callback.
The senseContext object's pushSenseContext() and popSenseContext() have been removed; they've been replaced with a new withSenseContext() method.

In addition, the callWithSenseContext() function has been changed to eliminate the argument list parameter; instead, callers should use anonymous functions when the code to be invoked requires arguments. (This is the way the rest of the library already used the function in most cases anyway, so this change simply cleans up some old code that's no longer needed. Most games won't need to call this function directly, so this is unlikely to affect any existing game code.)

The INSTRUCTIONS command now operates either in menu or in-line mode, not both. In the past, the menu format supplemented the in-line format, but this was redundant and complicated the user interface.
3.0.6h

Released June 7, 2003

Several major enhancements to the Actor class have been added. These changes are described in more detail in the comments in the library sources files, and in a new series of technical articles on the tads.org web site, Creating Dynamic Characters in TADS 3. We'll summarize the changes here, but you should refer to the articles for full details.

WARNING! Due to the extensive scope of these new features, there's a good chance that they'll undergo some changes as we gain experience with them. Be aware that game code that uses these features might have to be changed to accomodate library changes in future updates.

A new class, ActorState, makes it easier to create characters that exhibit multiple behaviors. The Actor class has been changed to work with ActorState; most of the methods involved in describing an actor's physical state and in interacting with an actor are now delegated from the Actor class to the ActorState object. The idea is that most of the parts of an actor that need to vary according to the actor's behavior have been moved to the new ActorState object; this means that adding a new behavior to an actor is a matter of defining a new ActorState object for the actor.

Several subclasses of ActorState have also been created. These include ConversationReadyState and InConversationState, which are used to make an actor explicitly enter, maintain, and exit a conversation; HermitActorState, for times when an actor is unresponsive; and AccompanyingState and AccompanyingInTravelState, which allow an NPC to travel with another character as part of the character's own turns.

A new "topic database" system makes it easier to manage conversational interactions, by creating individual objects that represent responses to ASK, TELL, GIVE, SHOW, and other interactions. The TopicDatabase class is used to store an actor's responses; Actor and ActorState inherit from TopicDatabase, so you don't normally create instances of this class directly. The TopicEntry class represents a response; several subclasses, including AltTopic, AskTopic, TellTopic, AskTellTopic, GiveTopic, ShowTopic, GiveShowTopic, YesTopic, NoTopic, DefaultTopic, and SpecialTopic are used to create the individual responses.

The conversationManager object and the ConvNode class are used to manage "threaded" conversations. A threaded conversation is one where an actor keeps track of what has been said before, and responds according to the context of the conversation.

The SuggestedTopic class and its subclasses let you maintain a "topic inventory," which is a mechanism that automatically suggests topics to the player. This is an optional mechanism that you can use in various ways; you can use it as a sort of hint system, and you can use it to guide threaded conversations.

Two library modules have been renamed: 'hints.t' is now 'hintsys.t', and 'menus.t' is now 'menusys.t'. Since these modules are included in most projects using the library file (adv3.tl), this shouldn't require any changes to any game code or project makefiles. The new names better reflect that these modules define the hint and menu systems, respectively, rather than actual hints or menus.
A new function, withActionEnv(func), makes it possible for daemon code to create a simulated Action environment. Some code relies upon there being a current Action object and an active command transcript object; these objects are normally only available while executing a command, which made it difficult to call code that depends on this command environment from within a daemon. This new function makes it easy to call such code from within daemons and other non-command contexts. The function sets up a dummy Action object and a default command transcript, then invokes the given function; the function can do anything that can be done during normal Action processing.
Openable now shows any special descriptions of the contents of an object that are revealed when the object is opened (via an OPEN command).
The English module now includes some needed customizations to the TopicAction class. These were missing in the past, which caused incorrect messages to be generated in some cases involving this type of action.
The new class RoomAutoConnector is a subclass of RoomConnector that can be mixed in to any BasicLocation subclass to make the room usable directly as a connector to itself. Room inherits from this class (it formerly inherited from RoomConnector and overrode part of the interface; the overrides are no longer necessary because they're contained in RoomAutoConnector itself now). In particular, RoomAutoConnector can be mixed in to any NestedRoom object's superclasses to make the nested room usable as a connector to itself.
The new notification methods beforeTravel(traveler, connector) and afterTravel(traveler, connector) are invoked just before and after a traveler travels via travelerTravelTo(). These notifications are sent to each object connected by containment to the traveler; beforeTravel() is called on each object connected by containment to the traveler in its old location, and afterTravel() is called on each object connected by containment in the new location.

These notifications are more precise than using beforeAction() and afterAction() with the TravelVia pseudo-action, because these actions are only called when travel is actually occurring. TravelVia will fire notifications even when travel isn't actually possible.

A beforeTravel() method can veto the travel action using "exit". The notification is invoked before the travel is actually performed, and even before a description of the departure is produced.

The method Actor.trackFollowInfo() is now invoked from Actor.beforeTravel() rather than directly from traveler.travelerTravelTo(), as it was in the past. This change makes the follow-tracking a lot less "special," in that it uses the general before/after travel notification system rather than a separate, purpose-built notification system. Note that this change means that any actor object that overrides beforeTravel() must inherit the base class implementation if the actor wants to be able to follow other actors.

The default travel precondition calculations have been changed slightly to allow connections between top-level rooms to be placed inside nested rooms. This allows, for example, a window that can only be reached when standing on a desk, or a secret passage from inside a wardrobe. The main changes are that TravelVia doesn't automatically require the traveler to be in the outermost room, but in the room containing the outbound connector; and travel between top-level rooms that leaves the traveler inside a nested room (as would happen when climbing through the window from outside and ending up on the shelf) now provides a normal arrival message.

First, BasicLocation.roomTravelPreCond() no longer does anything; the method is still present so that rooms can override it as desired, but it doesn't do anything by default. In the past, this method unconditionally added a precondition requiring that the traveler be in the outermost room; we don't want to apply that condition, since the required starting location of the traveler depends on the outbound connector, not the room.

Second, TravelConnector.connectorTravelPreCond() now adds a requirement that the traveler be in the connector's location, if the connector has a location. This ends up having the same effect as BasicLocation.roomTravelPreCond() in cases where explicit connectors, such as doors and passages, are situated in the top-level room.

Third, RoomAutoConnector.connectorTravelPreCond() overrides the inherited version, and instead requires that the traveler be in the "appropriate" starting location. This provides the same effect as the old BasicLocation.roomTravelPreCond(), but with an enhancement. The "appropriate" location is defined as the nearest enclosing location (enclosing the traveler) that has a directional connection ("north = xxx", etc) leading to the room. In most cases, rooms are connected directly to one another only at the top level; in such cases, the directional connection will be found in the traveler's outermost room, so the effect will be exactly as it was with the old system. But here's the enhancement: in cases where the directional connector is defined leading out of a nested room, this will correctly find the nested room as the starting location.

Note that NestedRoom objects cannot by default be used as direct targets of directional connectors, because they're not based on RoomConnector. It seems highly unlikely that it would ever be useful to connect a nested room directly to a directional connector (i.e., "east = wardrobe") - some kind of explicit connector (a door, or a passage, or something) seems desirable in almost every case, just from the perspective of game geography. In the event that a direct directional connector is required, though, simply include RoomAutoConnector in the superclass list for your specific nested room.

Fourth, the methods travelerArriving() and travelerLeaving() have been moved from Room to BasicLocation. This produces the proper travel messages for any travel between top-level locations, even when the travel starts or ends in a nested location.

BasicLocation.travelerArriving() now enforces the defaultPosture setting for the location. The default posture is enforced via a nested command before the arrival message is displayed. This makes it easier to create locations with restrictions on posture, such as a low crawl where you can only sit or lie down.

Note that if all goes well, there won't be any mention of the action for the player character (because the successful nested action will result in a default report, which will be suppressed due to the non-default message for the room description); so the new posture will simply be reflected in the description of the new location. Generally, all should always go well in these cases, since the actor will already be located within the location and merely has to change posture. If you want to explain why the posture change is necessary, you can override travelerArriving() for the location, display an explanation, then inherit the default. An appropriate message would be something like "The ceiling is so low you have to lie down."

The semantics of TravelConnector.getDestination() have been changed slightly. First, the method now takes a traveler rather than an actor, since connectors are always traversed by travelers. (A traveler might in fact turn out to be an actor, of course, but it could also be something else, such as a vehicle.) Second, the method is now required to return the destination, and the destination must be stable for the duration of the turn up to the point where the travel actually occurs.

To allow for connectors that are affected by traversal (for example, a connector that randomizes its destination each time it's used), the new method noteTraversal() must be called whenever the connector is actually traversed. This method can change the connector state as needed for the traversal. Note that game code should generally never need to call noteTraversal(), as the library dobjFor(TravelVia) action implementations do this automatically as needed. This method is provided for games to override as needed.

The base TAction and TIAction classes no longer require that any of the target objects of the command define an action() method for the verb. Instead, the classes now merely require that at least one of the target objects defines any of action(), check(), or verify(). If none of the target objects defines any of these methods for the verb, then the verb will automatically add an "illogical" status during the verify phase.

The purpose of this check is to ensure that the command has some sort of handling. If the game adds a new verb, but never defines any handling at all for the new verb for some object, then without this check, there would be no handling at all if the player attempted to apply the verb to that object. The old check tested for the existence of an action() handler, but this was overly conservative, since an object could have fully handled the verb in its verify() or check() handler. The new test allows for this by allowing the action to proceed if any sort of handler (check, verify, or action) is defined for an object involved in the command.

A new property, globalParamName, has been added to Thing. This lets you define a substitution parameter string (for "{xxx}" sequences) that can be used at any time to refer to the object in message text. Once you define an object as having a global parameter name, you can use that name to refer to the object in {xxx} sequences, even when no command is active, and even when the object isn't involved in any command. This can be especially useful for writing messages that refer to objects whose names change in the course of the game, such as actors who are known by one name until introduced, then are known by another name ("the white-haired man" might become "Bob" after he tells us his name, for example).
A new Script subclass, EventList, provides a convenient way of defining scripts using procedural code in steps without writing a big 'switch' statement in the doScript() method. The eventList property contains a list of script step elements. Each element gives one step of the script. An element can be a single-quoted string, in which case the string is simply displayed; a function pointer, in which case the function is invoked with no arguments; a ScriptEvent object, in which case the object's doEvent() method is invoked; or nil, in which case nothing happens on the step.
The Actor method getDefaultInterlocutor() has been renamed to getCurrentInterlocutor(), to better reflect its purpose. The conversation model keeps track of the actor we're currently talking to; conversational commands that aren't directed to a specific actor (such as ASK ABOUT BOOK) are assumed to be directed to the current conversational partner.
A couple of the abstract base classes in the area of Container have been renamed to better reflect their purposes. The class formerly known as BasicContainer is now called BulkLimiter, and the class formerly known as Encloser is now called BasicContainer. So, Container is a BasicContainer, which is a BulkLimiter; Surface is also a BulkLimiter. The new name BulkLimiter is more consistent with its main purpose, which is to constrain the aggregate bulk of its contents. The old name "Encloser" was confusingly similar to the real word "Enclosure," and was vague in conveying how the class differs from Container; the new name BasicContainer makes it clearer that this class contains some of the basic abstract functionality of Container but
The actor inventory listing mechanism has been reworked to make it a smarter about the listing format. When the inventory list is short, the listing will appear in a single sentence showing both the items being carried and the items being worn. When the listing is long, it's broken up into two sentences, as in the past. The threshhold for "long" is set with a property of the new DividedInventoryLister class, singleSentenceMaxNouns; the default is 7, which means that the listing will be shown in one sentence if it involves seven items of fewer, two sentences otherwise. For example:

  >inventory
  You are carrying a music box, a gold coin, and thirty silver coins,
  and you're wearing a watch and a helmet.

  >inventory
  You are carrying a cardboard box (which contains several LP's, a
  bunch of photographs, and some letters), a rubber chicken, two
  pieces of the Magic Goblet of Forblotsk, and a bowl of chowder.
  You're wearing a pair of flippers, a wool jacket, and a red wig.

The list length is determined by capturing the output and counting the phrase separators. The English library counts commas, semicolons, the word "and", and right parentheses to determine the phrase count.

This change slightly affects the Actor class (showInventoryWith now takes only one lister object), and extensively affects the InventoryLister class and the WearingLister class and their subclasses (the English-specific implementations in msg_neu.t). Games shouldn't be affected unless they modified these classes.

If you want the old behavior, where the items being worn and the items being carried were always shown as separate sentences, simply set DividedInventoryLister.singleSentenceMaxNouns to zero. If you want to always use a single sentence no matter what, set the property to some absurdly high number - 30000, say.

If you'd prefer the more traditional behavior that shows the items being worn marked "(being worn)" and mixed in with the rest of the inventory in a single listing, simply change Actor.inventoryLister to refer to actorSingleInventoryLister.

The match list for an ALL phrase in player input is now filtered through the filterResolveList() method of each object in the list, just as ambiguous noun phrases are. This allows objects that substitute for others in resolution lists (such as Collective and CollectiveGroup objects) to perform the same substitutions in ALL lists that they would in normal matches.
The miscWord grammar rules are now defined in en_us.t, rather than in the language-independent parser.t as they were previously. This change is necessary because different languages might have different token types that are valid in miscellaneous word lists. (In point of fact, the English parser defines a special language-specific token, tokApostropheS, that is now allowed in miscellaenous word lists.)
The menu system now uses the regular game color scheme as the defaults; the top "instructions" bar is shown using the status line color scheme, and the main menu area uses the normal game window color scheme. This scheme is the safest default, since it uses only colors the user has selected (on interpreters that allow the user to select the color scheme); this ensures in particular that things like hyperlink colors work well with the menu colors, since presumably the user will have chosen settings that work well together. (If not, at least it's not the game's fault.) This change means that menus won't stand out as well from the main game window, since they use the same color scheme, so the default mode for menus has been changed to the "full screen" mode. This new default appearance - full screen, using normal game and status-line colors - makes menus very unobtrusive, since they just look like an ordinary game screen.
The menu system takes advantage of the new MORE-mode banner style option, by using a separate banner to show "long topic" items. Since long text can now safely be displayed in a banner, with the interpreter providing pagination via MORE prompts as needed, long topics can avoid taking over the main game window. This is nice because it leaves the original game window intact (without any need to clear the screen) after the user exits from the menu system.
The MenuItem class has a new property, heading, that specifies the text to display as the heading of the menu while the menu is active. By default, the heading is the same as the title, which is the string displayed in the parent menu's list of items. The separate property allows the caption shown while the menu is active to differ from its title, if this is desired.
A couple of the English messages for the npcMessagesDirect group have been recast as quoted statements from the NPC, for consistency with other messages in the group. The affected messages are noMatchDisambig and disambigOrdinalOutOfRange. In addition, askDisambig (in the same group) has been tweaked slightly to accomodate this change.
The CollectiveGroup class has been split into two classes. The full behavior of the old class is now contained in the class ItemizingCollectiveGroup, which is a subclass of CollectiveGroup. On Examine, the CollectiveGroup class no longer shows the itemized list of collected items, but instead simply shows its own description using the normal Examine handling. The itemizing behavior is desirable in some cases of collective groups, but certainly not all; this change makes it easy for the game to select whether or not the itemizing behavior is used.
If a check() routine (in a dobjFor() or iobjFor() group) terminates the command with 'exit', the parser now automatically marks the action in the transcript as having failed. This is the same thing that the reportFailure() macro does, so this means that it's never necessary to use reportFailure() from within a check() handler. Since the purpose of check() is to enforce conditions on the action, exiting from the check() routine necessarily means that the command has failed; this change takes advantage of that to save games a bit of work.
A new set of grammar productions provide for third-person reflexive pronouns in verbs with two noun-phrase slots: "put klein bottle in itself," for example. These are mostly intended for conversation, specifically things like "ask bob about himself," but will work in general for the rare cases where they might be useful.

The parser resolves a third-person reflexive pronoun by referring back to the resolved object list for the other noun phrase in the action. Verbs taking only one noun phrase don't accept these, as they make no sense: "open itself" isn't meaningful. In the basic TIAction, the order of noun phrase resolution can vary by verb, and the order of resolution isn't required to match the order of the nouns in the phrase; in English, at least, a reflexive pronoun in this type of construction is always anaphoric (i.e., it always refers to a phrase earlier in the sentence). This means that the TIAction resolver could find itself trying to resolve the reflexive phrase first, before it knows the resolution of the phrase to which the reflexive refers. To cope with this situation, the resolver notes when this occurs, provides an empty list for the initial resolution of the reflexive, and then goes back and re-resolves the reflexive after resolving the other noun phrase.

The new Thing property isKnown lets you specify that an object is known in advance to the actors in the game. The method isKnownBy(actor) can be used to test actor knowledge of a Thing: isKnownBy(actor) returns true if seenBy(actor) returns true, or the isKnown property is set to true for the object. The Actor methods knowsTopic() and isLikelyTopic() now use isKnownBy() rather than seenBy() to determine actor knowledge.
A few new conversation-related verbs have been added: Goodbye, Yes, and No. These are handled similarly to Hello.
A new Action method, isConversational(issuingActor), determines if an action is "conversational." A conversational action is one that involves the issuing actor saying something, within the game context, to the target actor, as opposed to an order to the target actor to do something. Of the system-defined verbs, Hello, Goodbye, Yes, No, and TellAbout (with the issuing actor as the direct object) are defined as conversational.
The interface of the Actor.obeyCommand() has changed, as has its meaning. The method now takes two parameters: the actor who issued the command, and the Action object. In the past, this method was called with only the issuing actor as a parameter, because the action was unresolved when the method was called. Now, the parser calls this method after resolving the action and its objects. This change gives the method full access to the details of the action, so it can decide to accept or reject commands based on the actual action being performed.

The parser does not call this method when the action is "conversational," as indicated by the isConversational() method no the action. Conversational methods are not considered to involve an order to the target actor. The actual physical action of a conversational action simply consists of the issuing actor saying something to the target actor, so even though these actions are phrased as though they're orders to the target actor, they're really carried out by the issuing actor, and thus don't require acceptance by the target actor.

The default implementation of this method on Actor calls the corresponding method on the atcor's current state object. The basic state object implementation simply refuses the command, so the default behavior is the same as it was in the past.

The TextList subclass formerly known as SlaveTextList has been renamed to SyncTextList, to better reflect that this list is always kept synchronized with its associated master list. The old name suggested that the connection was one-way, that state changes flowed only from the master to the slave, when in fact the master and slave are fully synchronized. The new name is more suggestive of this two-way connection.
A new TextList subclass, StopTextList, provides a minor variation on the standard text list script. Once StopTextList reaches its last message, it will simply stay at the last message forever, repeating the last message each time the script is invoked. This is useful for cases such as a conversation topic where an actor has several things to say, but once the actor has said each bit, the actor will from that point on just repeat the last message; the last message would usually be something like "I've already told you all I know about it," or could be a summary of what the actor revealed.
ShuffledTextList features a few enhancements.

First, a new property, firstStrings, can be set to a list of strings to show sequentially before starting the shuffled strings. This can be useful in cases where you have some meaningful information to convey initially, but once those messages have been displayed, you want to fall back on randomized messages for atmosphere and variety. The firstStrings list is shown only once, in sequential order. Once the firstStrings list is exhausted, these strings are never shown again.

Second, the main shuffled list in textStrings can now be shown sequentially the first time through, if desired. Set the property shuffleFirst to nil (it's true by default) if you don't want the list shuffled the first time through. Since the strings in the textStrings list are intended to be shown in random order, in most cases it won't matter to the author what order is used, and in this sense the order in which the strings are actually defined is as random as any other order. In some cases, it might actually be desirable to have the strings come out in a certain order the first time through; this lets you refer back to an earlier message in a later message, for example, with assurance that the player will have always seen the earlier message first. After the first time through the list, the list is always shuffled and shown again in random order.

Third, the class now takes care to ensure that a message is never repeated consecutively. Repeats are normally avoided naturally by the shuffling: every item is shown once before anything is repeated. But a consecutive repeat was still possible in one special case, which is immediately after a shuffle. Because the order of one shuffle is independent of the order of the next shuffle, it was possible for the last element of the previous shuffle to be the same as the first element of the next shuffle. The class now suppresses this case by checking each new shuffle to make sure its first element doesn't match the last element of the previous shuffle, and choosing again when necessary. This change is intended to increase the apparent randomness by ensuring that the same string is never shown twice in a row.

The special description list order has been modified slightly. If two objects have the same specialDescOrder value, but one of the two objects is inside the other, we'll list the outer object first. So, specialDescOrder still dominates, but when there's no specialDescOrder preference, we'll list containers before their children. In almost all cases, this produces a more pleasing list order; objects within other objects will frequently mention their placement in their special description text, so it's usually better to have seen the containing item's own special description before we see the containing item mentioned in a child item's special description.
A set of new commands makes it easier to record and play back command scripts, which can be especially useful while writing and testing a game. The command RECORD (RecordAction) starts recording a command script, which saves command input text to a file. RECORD OFF (RecordOffAction) turns off the recording starting with RECORD (it has no effect if no recording is in progress). REPLAY (ReplayAction) plays back a command script previously recorded. RECORD and REPLAY both accept a filename in quotes on the command line, but this is optional; if you just type RECORD or REPLAY, the commands will ask you to select a file using a standard file dialog.

REPLAY has two mutually exclusive options. REPLAY QUIET plays back the script without showing any output while the script is running. REPLAY NONSTOP plays back the script without pausing for MORE prompts.

Because REPLAY is fully redundant with the old "@" syntax, but much friendlier, the "@" syntax has been removed.

The new class LocateInParent makes it easy to define a nested object that's to be located within the enclosing object. This is a mix-in superclass, so simply add it to the object's superclass list; LocateInParent should go ahead of Thing or any Thing subclass in the superclass list.
3.0.6g

Released April 12, 2003

The default object picker has been improved to better handle cases where commands are remapped. Remapping from one object to another is often used as a convenience to the player, so that the same command can be applied to any of several related objects with the same effect. For example, you might want to set up a jar with a lid so that OPEN JAR and OPEN LID have the same effect; this could be done by remapping OPEN LID to OPEN JAR. Similarly, a house with a door might remap ENTER HOUSE so that it's handled as ENTER DOOR. In the past, cases like these prevented the parser from choosing a default object, because the parser can only apply a default when there's only a single object that could make sense - the remappings made several different objects look equally good superficially, even though the apparent different possibilities were all going to turn into the same thing in the end thanks to the remapping.

The parser now keeps track, during the verification process, of any remapped objects. The default picker looks at this remapping information before deciding on a default. If there are any objects among the possible defaults that are to be remapped, the default picker will discard any that are redundant due to the remappings. For the OPEN JAR/OPEN LID example, the parser would see that OPEN LID turns into OPEN JAR, and it would thus discard the lid from the list of possible defaults, since the jar is already in the list. This would leave us with just one possibility, so the parser would be able to apply use it as the default.

The parser will only eliminate a remapped object as redundant when the remapping matches the object, action, and role of another object in the list of possible defaults. If a remapping changes the verb, the remapped object will only match another object if it's also remapped to that same new verb. The verb has to match because the command could otherwise have a different effect, and thus the two actions on the same object wouldn't be redundant with one another.

The asExit() macro has been changed slightly. You should no longer uses the ":" syntax to define the direction; instead, simply put the asExit macro directly after the direction name:

  north asExit(down)

This change is intended to make asExit more consistent with the similarly-named asDobjFor() and related macros.

The menu system has a few minor changes to make the user interface more easily customizable.
  • In HTML interpreters, the top title/instructions bar now shows a hyperlink that returns to the parent menu. The text in this hyperlink is controlled by the 'prevMenuLink' property, which by default uses the text 'Previous'.
  • In HTML interpreters, a hyperlink is shown in topic lists to advance to the next topic item. The text of the hyperlink is given by the 'nextMenuTopicLink' property.
  • The 'fgcolor' and 'bgcolor' properties now look to the parent menu, if there is a parent menu, for their default values. For the top-level menu, the defaults are still the status line colors. In most cases, you'll want all of the menus in an entire menu tree to have the same appearance, and this change makes it easy to accomplish this: simply specify the appearance in the top-level menu, and all of the child menus will use the parent menu settings. Of course, individual menus can sever this parental dependence simply by overriding these properties.
  • Two new properties, 'topbarfg' and 'topbarbg', allow each menu to specify the foreground (text) and background colors of the top title and instructions bar. By default, these look to the parent menu, if there is one; the top-level menu defaults to using the inverse of the color scheme of the menu itself.
  • The 'indent' property uses the parent's value by default.
  • A new property, 'fullScreenMode', lets you indicate that you want the menu to take over the entire interpreter window. By default, menus are given just enough space at the top of the interpreter window to display their contents. If 'fullScreenMode' is true, though, menus will cover the entire interpreter window. Full-screen mode has the advantage that it's less jumpy than the default partial-screen mode, since the partial mode resizes the menu windows on each navigation operation to accomodate the new contents.
A new hint menu type has been added: HintLongTopicItem. This is simply a MenuLongTopicItem subclass designed for use in hint menus. (Regular HintLongTopicItem objects don't have the necessary logic for calculating visibility in a hint menu, so the specialized subclass should be used for long-topic menus within hint menu trees rather than the base MenuLongTopicItem type.)
The library's turn-counter incorrectly counted a command as two turns if the command's action() handler invoked a nested action which was then remapped (via remapTo). Nested actions aren't supposed to count as separate turns; it was erroneous for the remapping to affect this one way or the other. This has been corrected.
moveInto(nil) now works properly for a MultiLoc object. (In the past, this incorrectly caused a run-time error.)
SensoryEmanation has a new property, isAmbient. This is nil by default; if set to true, it indicates that the noise/odor/etc is purely in the background, so it's not especially noticeable. Ambient emanations won't be mentioned when they first become sensed; normally, an emanation is automatically mentioned whenever conditions change so that it was not sensed previously but is now. Ambient emanations will still be mentioned in explicit intransitive LISTEN/SMELL commands; they simply won't be mentioned on their own.
Part of the Container class has been separated into a new lower-level base class, Encloser. An Encloser is an object that can enclose its contents, so that all senses must pass through the encloser's material when passing in and out of the object. Encloser has all of the basic handling for enclosing contents, but not the action handling. Encloser is meant for cases where the object's contents are not to be directly manipulable by the player, via "put in" commands and the like. Container is now a subclass of Encloser; Container defines the suitable action handlers to allow a player to manipulate the container's contents.

A new mechanism in the Actor class makes it relatively easy to set it up so one or more non-player characters accompany the player character on travel. This kind of group travel is especially good for things like a sidekick character who goes everywhere the player does, and for tour guides or other escorts who are showing the player character where to go.

This mechanism is similar to the "follow" mechanism, which makes one actor attempt to follow another on each turn, but it improves considerably on regular following by customizing the messages. Normal following is a little awkward for explicit group travel of the sort that one wants with sidekicks and escorts, because the messages are so generic; the NPC almost seems to be wandering around on its own and just coincidentally showing up where the PC is. This new "accompanying" mechanism accomplishes much the same thing, but smooths out the messages a bit. First, rather than having the NPC trailing along after the fact, the new system sends accompanying NPC's on ahead; this means that the NPC's don't just wander in later as they do with normal following. Second, the message for the NPC's pre-departure is customized to make it clear that the NPC isn't departing, but is coming along with you. Third, on arriving in the new location, there's a customization hook for describing the NPC's presence in the new location specially for the group travel; this is important because it gives us a place to mention that the actor starts doing whatever it is the actor normally does in the new location. The overall effect is that each group travel action is made to look like a single, integrated action, rather than two generic and unrelated actor travel actions.

NPC's can also be set to accompany NPC's with the same mechanism, but it's much less interesting for that, since the main value of the new mechanism is that it improves the messages for PC-plus-NPC group travel. For NPC-plus-NPC group travel, this new mechanism has no particular advantage over the the simpler "follow" mechanism.

Setting up accompanying travel is relatively straightforward. You have to specify the conditions under which the group travel occurs, and you should customize two messages related to the travel. You set this all up by overriding methods on the NPC who's going to follow the lead actor (usually the PC). The methods to override are as follows.

First, override the accompanyTravel() method so that it returns true under the conditions where you want the accompanying travel to occur. This method is called each time your NPC is present and sees another actor attempt a travel action (it's called during your NPC's beforeAction when the action is a TravelVia). If you want your actor to accompany the PC everywhere, simply return true when the action actor is the PC. The method also has access to the TravelConnector involved in the travel, so you selectively accompany an actor for some destinations but not others, if you wish.

Warning: the next two parts are likely to be modified soon. I'd recommend against writing any code that makes customizations using these features right now.

Second, define an accompanyDesc method for your actor. This method is used instead of the usual actorHereDesc method to describe your actor in the new location immediately after the group travel is finished. It's usually desirable to use a special message to describe the actor right after the accompanying travel, because you usually want to convey that the actor is just arriving - not that the actor is walking in separately from the lead actor, but that the actor is arriving at the same time as the lead actor. If you do override accompanyingDesc, you should override accompanyingListWith to return an empty list ([]).

Third, you can optionally provide a special TravelMessageHandler for your actor by overriding getAccompanyingTravelMessageHandler. By default, this routine provides a message handler that uses messages like "Bob goes with you" instead of the usual "Bob leaves to the east" to describe the departure of your actor. The normal departure message isn't appropriate, because the actor isn't just leaving, it's leaving *with* the lead actor. The default "Bob goes with you" is better, but you might still want to override this handler to provide even more customized messages: "Bob escorts you to the east," or "You drag Bob with you," or whatever makes sense for the specific situation.

Finally, note that you can use accompanying travel and the regular following mechanism together. Regular following can be a useful fallback for cases you don't want to customize. Regular following occurs after the fact, because it occurs on an NPC's turn when the NPC sees that the actor it's tasked to follow is no longer present. Accompanying travel, in contrast, happens on the same turn as the lead actor's travel. This means that regular following is essentially overridden by accompanying travel, since it happens first.

The English parser now accepts quoted strings as adjectives. Quoted strings can be used as adjectives in exactly the same way that numbers can be used, so phrases such as "QU" TILE and BUTTON "G" are accepted.

For the purposes of matching vocabulary in the dictionary, quoted adjectives are simply treated as though the quotes weren't present. So, "QU" TILE is treated exactly the same as QU TILE. This means you don't have to worry about the quotes when defining your vocabulary words.

There is an additional bit of special treatment for quoted strings. The special vocabulary word '"*"' (that is, an asterisk within double-quote characters) serves as a wildcard for any quoted string. If an object defines this special wildcard as an adjective among its vocabulary words, then that object will match any quoted string as an adjective. This is the equivalent of the '#' wildcard string for numeric adjectives.

String-as-adjective phrasing is implemented using the new production literalAdjPhrase. This new production is now used wherever numberPhrase was formerly used in the role of an adjective in noun phrase rules. literalAdjPhrase matches numbers, '#' number phrases, and quoted strings.

In travel.t, there were formerly a couple of TravelConnector subclasses that handled action synonyms for TravelVia using asDobjFor(TravelVia). These have been changed to use remapTo(TravelVia) instead.

The asDobjFor() approach had the drawback that the action synonyms didn't ever generate TravelVia actions proper, but simply called the TravelVia handlers internally; so, for example, beforeAction() routines wouldn't ever see a TravelVia action being performed. The new remapping approach is better because it means that every possible action on these objects that involves travel will go through an actual TravelVia action. This allows beforeAction() and similar routines to be assured that they can catch all travel actions by looking for TravelVia alone, eliminating the need to worry about synonym actions that could cause travel.

A couple of new classes simplify a few tasks involving entry and exit portals. Exitable is a new class that's similar to Enterable, but provides Exit instead of Enter as its main action. EntryPortal and ExitPortal are just like Enterable and Exitable, respectively, but add support for GoThrough actions.
Added finishOptionFullScore, to make it easy for the game to offer the player the option of seeing the full score at completion.
3.0.6f

Released March 23, 2003

It's now less dire for gPlayer.location to be set to nil. In the past, setting gPlayer.location to nil caused an infinite error loop, because the status line code dereferenced gPlayer.location without checking for nil, which caused an error, which aborted the current turn, which started a new turn, which tried to update the status line, and around we went. It's still not completely valid for gPlayer.location to be nil; this change just removes the infinite error loop, which makes it a little easier to deal with this problem arising during programming and testing.
Changed Thing.dobjFor(Remove) to remap to RemoveFrom (asking for an iobj). Wearable now overrides Remove to map it to Doff. It makes more sense in general for "remove foo" to mean "remove foo (from something)" rather than "doff foo"; only when "foo" is an article of clothing does the "doff" interpretation usually apply.
Added the new TravelConnector property isConnectorListed. If this property is nil, the connector is suppressed from generated exit lists (such as the status line exit list, and "you can't go that way" messages).
Added a new class, UnlistedProxyConnector, that acts as a proxy for an underlying connector, but is unlisted. The idea is to make it easy to add an exit that acts exactly like another connector, but is suppressed from exit lists.
Added a macro, asExit(), that can be used to define an UnlistedProxyConnector that links to another direction exit.
Integrated Stephen Granade's menu system (menus.t) with the main library.
Added an on-line adaptive hint system (hints.t).
Added the new class RestrictedContainer. This is a Container specialization that makes it easy to define containers that only accept certain objects as contents. In the basic implementation, the allowable objects are simply enumerated as a list of objects; but this can be easily overridden for other methods of determining which objects are allowed.
Added the new class SingleContainer, and the associated new precondition objEmpty. SingleContainer is a specialization of Container that only allows a single object to be in the container at a time; this is suitable for things like sockets and receptacles. SingleContainer places the objEmpty precondition on the PutIn action; the objEmpty precondition simply requires that the subject object is empty, and tries implicit TakeFrom actions on the contents to accomplish this.
In en_us.t, changed typographicalOutputFilter.eosPattern to ignore any run of HTML tags between sentence-ending punctuation and a following space.
The main implicit action processor now uses the more abstract "allowed in implicit" test on the verify results, rather than merely checking for "dangerous" results. This will allow results flagged as "non-obvious" to cancel implied actions, as they should. (A non-obvious command should never be done implicitly, because implicit commands are specifically for actions that are obvious intermediate steps toward the explicitly stated command.)
Openable and Lockable have been cleaned up slightly. A new class, Linkable, has been added for objects that can be paired in master/slave relationships; Openable and Lockable are now based on Linkable. This removes some duplicated code for managing the masterObject relationship. In initialization, Linkable checks for a master object loop (where each object points to the other as the master) and break such loops by arbitrarily selecting one as the master. Also, we have reduced the number of times we have to refer to masterObject by relying on isOpen/isLocked/makeOpen/makeLocked to defer to the master. Also, the initiallyOpen/initiallyLocked initializations are now handled in initializeThing() rather than in isOpen/isLocked.

Another new class, BasicOpenable, provides the basic state management for openable objects (which can optionally be linked in pairs to maintain the same status across the pair) but doesn't provide any of the verb handling of Openable. This can be used for objects that want to maintain open/closed state using the usual method names, but which don't respond to direct player open/close commands.

IMPORTANT: Objects based on Openable and Lockable must not initialize their open status with isOpen or their locked status with isLocked. Instead, initialize the status with initiallyOpen and initiallyLocked, respectively. Also, be sure you never set isOpen or isLocked directly; instead, call makeOpen() and makeLocked() to effect these status changes.

Changed Passage slightly to rely on isOpen/etc more to manage the masterObject relationship.
Added a parameter to Direction.defaultConnector() giving the location from which travel is being attempted. This allows the direction to customize the default connector according to the type of location.
Added a new property, isShipboard, to Thing. By default, if we have a location, we use the location's isShipboard setting, otherwise we return nil. Rooms aboard ships can set this to true to indicate that shipboard directions make sense here.

In ShipboardDirection.defaultConnector, if the source location or any container has 'isShipboard' set to true, then we now use noTravel as the default connector rather than noShipTravel, as the latter is meant to convey that shipboard directions make no sense in non-shipboard locations.

Added a ShipboardRoom mix-in class that defines isShipboard to true.

Changed the way that VerifyResultList determines if an action is allowed at all or allowed as an implicit command. In the past, this calculation did what the "most disapproving" result in the list said to do; this wasn't quite right, because approval is essentially a separate axis from the "disapproval" order, despite the name. In reality, the "disapproval" order is the message priority order, and the actual determination of approval depends on there being no disapprovers. So, to determine approval, we now scan the full result list and require that every result approves; a single disapproval, no matter where it appears in message priority order, constitutes disapproval.
In the English module, added a new token type for numbers specified with a leading pound sign (as in "#2 pencil" or "room #101"). These are treated essentially the same as numeric adjectives. These match vocabulary for just the underlying numbers, not including the pound signs, so vocabulary should be specified simply as '2 pencil' and '101 room'. (In other words, do not include a pound sign in vocabulary words; just use the number as with ordinary numeric adjectives, and the parser will match the number with or without the pound sign).
The TravelVia check() condition in Passage no longer uses isOpen as its test. Instead, it calls the new abstract condition method, canActorTravel(), passing the current actor as a parameter. This makes it easy to allow some actors to pass and not others, and to test for conditions other than isOpen. By default, canActorTravel() simply returns isOpen, so this doesn't change the default behavior.

Along the same lines, Door.connectorTravelPreCond() now calls a separate method, getDoorOpenPreCond(), to obtain the door-is-open precondition object. By default, this returns a doorOpen precondition (wrapped with an ObjectPreCondition for 'self'), so the default behavior hasn't changed.

In the past, when an NPC was following another actor, and the actor being followed moved into a nested room within the current room, we generated a redundant message:
>stand on platform
Okay, you are now standing on the platform.
Bill stands on the platform.
Bill follows you onto the platform.

The redundant "Bill follows you" has been eliminated.

The reporting mechanism now processes sets of implicit action announcements to make them more readable and more easily understood. In the past, each implicit action was shown separately; when one implicit action triggered another, this could make for somewhat confusing displays. For example, in the Platform Room in sample.t, if the PC is sitting on the blue chair and wants to get up and sit on the red chair, we formerly generated a transcript like this:

>sit on red chair
(first standing on the red platform)
(first getting off of the blue platform)
(first standing)
Okay, you're now sitting on the red chair.

To the uninitiated, this looks backwards: shouldn't we stand up, then get off the blue platform, then get on the red platform, and then sit on the red chair? Of course, that's what's actually happening, and in its own way the transcript above reflects this: the second "first" applies to the first "first": it's saying "before you can stand on the red platform, you first have to get off the blue platform." Likewise, the third "first" applies to the second "first," to say "before you can stand on the red platform, you have to stand up first." In other words, the implied reports are nested recursively. The recursive structure isn't represented visually, though, so it's not evident whether one of the later "firsts" refers to the preceding "first" or back to the original command line. It would have been possible to represent the recursive structure visually, using indentation or something like that, but this probably wouldn't have made most people very happy; most non-programmers aren't accustomed to thinking in terms of recursion and stacks and tree views, and even programmers might well have found this kind of presentation to be too obviously mechanistic.

To improve the situation, the transcript processor now features a new transform, called implicitGroupTransform, that rearranges these recursive listings into an order that should be entirely straightforward to anyone, programmer or not. The new transform also consolidates these lists into a much more concise and readable format. The transformer does two things. First, it "unstacks" recursive lists of implied action announcements to put them into the chronological order in which the commands were actually performed; the transcript has always kept track internally of which announcement is tied to which action, so the new transformer can merely inspect these records to determine the action relationships and use this information to unwind the stack. Second, the transformer combines each run of adjacent implied action announcements into a single announcement, showing a list of the actions performed. The result is that the example above now looks like this:

>sit on red chair
(first standing, getting off of the blue platform, then standing on
the red platform)
Okay, you're now sitting on the red chair.

This new format should be easier for players to understand, since it shows implied actions in the order in which they're actually carried out, eliminating any need to be aware of the recursive goal-seeking work that the system is doing internally. Hopefully, players will also find it more readable and less obviously mechanistic than the old format.

3.0.6a-e

3.0.6e was released March 16, 2003

For a restore-on-startup operation, if the restore fails, don't simply start the game from the beginning. Instead, offer options to START from the beginning, RESTORE another game, or QUIT, using the same type of prompt that we use for presenting "finishing" options.
Add a verb for OOPS typed at arbitrary times, which simply explains that OOPS can only be used after the parser has pointed out an unknown word.
Fix problem using possessive qualifiers in topic phrases. (The problem is that we're using the topic resolver to resolve qualifiers in the topic phrase; we should be using an ordinary object resolver. Change the topic resolver's qualifier resolver to use the topic action's direct object resolver by default.)
Move the pronoun-setting routines (setPronoun, setIt, setHim, setHer, setThem) into Actor - these shouldn't be global functions since they need to operate on the current actor.
In TopicAction, set the pronoun antecedent based on the direct object phrase immediately during resolution, to allow things like "ask bob about his book."
Add a DefineIAction macro, for defining IAction classes.
Base LiteralAction on IAction, not directly on Action. Because of this change, rewrite OopsAction to implement its processing in execAction() rather than doActionMain().
Refactor the Instructions action into an InstructionsAction base class and a separate grammar rule, to allow the base class to be referenced by name from other code.
Add a banner manager, along the lines of the input and output managers. A new BannerWindow class would represent a banner; this would encapsulate the system banner handle and provide methods that operate on the banner. The game should use the BannerWindow methods to perform all operations on banners, rather than calling the system-level banner API directly. The banner manager should provide persistence support, so that the on-screen banner layout is restored on RESTORE, UNDO, or RESTART. To this end, BannerWindow should be an ordinary persistent object, and a separate set of transient objects should track the current UI state. On RESTORE, UNDO, or RESTART, the transient tracking list and the persistent BannerWindow states should be compared, and the actual on-screen UI state, as represented in the transient objects, should be brought into line with the saved state.
Wait to mark an object as 'seen' in Thing.lookAroundWithin() until just before the method returns, so that any routines called from the method can check the old 'seen' status.
Fix gAction reference in Actor.travelWithin() so that it's conditional on gAction being non-nil.
Fix takeFromNotInActor message (iobj/dobj are reversed).
Fix TAction.retryWithMissingDobj() and TIAction.retryWithMissingIobj() so that they cancel the game time contribution of their enclosing (replaced) actions.
Fix touchObj precondition logic so that it realizes when an attempt to remove an obstruction with a precondition fails. To do this, keep track of which obstructions we've tried to remove with implicit commands, and give up if we encounter the same obstruction more than once.
Add <.parser> to msg_neu.t responses for some system verbs (NOTIFY ON/OFF, EXITS ON/OFF, a few others).
Assign the IndirectObject role to the literal in a LiteralTAction by overriding getRoleFromIndex, getObjectForRole, and getMatchForRole. These are needed to allow remapTo to be used with a LiteralTAction.

Note that the literal is always in the IndirectObject role, regardless of whichLiteral, which only specifies the grammatical role for message generation purposes.

Likewise, assign the IndirectObject role to the topic in TopicAction. This will allow remapTo to be used with a TopicAction.

Rename TopicAction to TopicTAction, parallel to LiteralTAction.
Add a new TopicAction that takes only a topic as its object, parallel to LiteralAction.
Rename whichObject, whichLiteral, and whichTopic to whichMessageObject, whichMessageLiteral, and whichMessageTopic, respectively, to make it clearer that these apply only to the roles played in generated messages, and not to any other roles.

In particular, the message role only affects the way the object is used in generating messages based on the verb, such as "what do you want to open it with?".

It would be better to interpret "type on typewriter" as having a missing literal than as having a missing ON phrase (in other words, we want to interpreter this as TYPE <empty literal> ON TYPEWRITER rather than as TYPE "ON TYPEWRITER" <on missing object>).

To do this, add a verb rule for TYPE ON <object>, without any literal phrase. For most objects, fail in verification; but when verification passes, in the action, ask for a missing literal phrase. (This has the additional benefit that it makes it easy to create objects that allow generic typing on them, as in TYPE ON COMPUTER, for situations where the game doesn't want to make the player type anything specific but still wants to allow the generic act of typing on the object.)

>ASK BOB ABOUT
what do you want to ask him about?

>BILL
"Ah, yes, , very interesting..."

(The problem is that we're not propagating getOrigText() from the empty topic phrase match down to the underlying replacement match.)

>ASK BOB
what do you want to ask him about?

>ABOUT BILL
"Ah, yes, about bill, very interesting..."

(The problem is that we need an aboutTopicPhrase production to parse responses to ABOUT WHAT questions.)

Add a generic Settable class for things (such as dials) that can be set to various settings. Base Dial on Settable.
Do not override lookAroundWithinDesc() in BasicLocation; instead, provide an implementation of BasicLocation.roomDesc that simply uses the 'desc' property to display the room's interior description. (This allows nested rooms to differentiate more easily between interior and exterior descriptions, if they want to.)
Add a new method to Thing, filterResolveList(), to allow "global" filtering of a noun phrase resolution list. Call this method from the parser where we use 'verify' to filter a resolution list. Unlike 'verify', this new method has access to the entire resolution list; this allows the method to take action based on which other objects are in the resolve list.
Add a new mix-in class, Collective, to serve the purpose of the former isCollectiveFor(obj) method in Thing. To create an object like the matchbook in the sample game, use Collective as an additional base class.

Remove the special-cased plural filtering in AllPluralProd and DefinitePluralProd. Move this logic instead into Collective's filterResolveList() implementation.

Add a new class, CollectiveGroup, to allow creating "abstract" collective objects. These differ from regular Collective objects in that a Collective is actually a simulation object (such as a matchbook that can hold several matches), whereas a CollectiveGroup is not a separate object from the player's perspective (so it's never listed as a separate object in a room's contents listing, for example).

Add a new Thing property, collectiveGroup, that allows an ordinary object to be associated with a CollectiveGroup object. In CollectiveGroup's filterResolveList() method, choose to keep either the individuals (the ordinary Thing objects associated via their collectiveGroup properties with a CollectiveGroup object) or the CollectiveGroup in the resolution list, but not both. By default, make the selection based on action; subclasses can override as desired to use other criteria.

CollectiveGroup can be used to create things like a "money" object to represent a set of coins and bills, so that a command like "look at money" can respond with a single description of all of the money present, rather than iterating over all of the coins and bills individually.

Eliminate the actorHereLister. Instead, handle actor descriptions using the special description mechanism. By default, give actors a higher specialDescOrder, to keep actor special descriptions listed after other special descriptions.
Add a new special description control property to Thing, specialDescBeforeContents; set it to true by default in Thing, but override it to nil in Actor. In verbose room descriptions, show special descriptions in two phases: where we currently show special descriptions, just before the room's list of portable contents, show special descriptions only for items with specialDescBeforeContents set to true. Then, after we've shown the portable contents list and any other sensory messages (listen/smell), show the special descriptions for objects with specialDescBeforeContents set to nil.

This will allow the traditional ordering, with actor "I am here" messages placed after the rest of the room description, while keeping the rest of the special descriptions grouped with the room's main description. Most special descriptions belong with the main room description because they're meant to be extensions of the room description. Some special descriptions are not; they're meant to describe more ephemeral status information, so work better when grouped with the other status-like messages. Actors in particular fall into the latter category, since actors are meant to seem autonomous, not parts of the rooms they occupy.

When showing special descriptions of the contents of an object as part of the object's description, use a new form of the special description method, showSpecialDescInContentsWithInfo(), and corresponding specific methods for viewing conditions (showSpecialDescInContents, showObscuredSpecialDescInContents, showDistantSpecialDescInContents).

Override showSpecialDescInContents in Actor, so that we can show the type of description we previously showed using nestedActorLister.

Remove nestedActorLister and NestedRoom.examineNestedRoomActors().

Add a default output filter, typographicalOutputFilter, to the main output stream. In this filter, convert '--' and '---' sequences to typographical dashes, and insert an en space at the end of each sentence.

The conversion of sentence-ending punctuation compensates for the elimination of the double-space insertion in the VM-level formatter. It also improves matters over the old way by making the treatment of sentence-ending punctuation customizable: the game can replace the filter method with its own custom conversions. This allows customization not only for stylistic variation but for language-specific conventions as well.

Add the special {subj} messageBuilder token. This token simply notes its target object as the subject of the sentence, for internal book-keeping purposes, but doesn't actually display anything.
Add a "log file console" type. This would act like the main console or a banner console, in that it would apply the full set of VM-level output formatting (including text-only HTML interpretation) to the text sent through the stream, but it would write the output only to a file, rather than displaying it anywhere. This would be useful for capturing output to a file without showing the output at all on the display; for example, this would make it easy to generate an About.txt file by capturing the output of the ABOUT command to a file during preinit, without having the ABOUT output also show up on the screen.

This would require a new set of VM-level functions in the tads-io set:

  • logConsoleCreate(filename, charmap, width): returns a handle to the new console
  • logConsoleSay(handle, ...): writes the arguments to the log console; works like say() and bannerSay()
  • logConsoleClose(handle): closes the console

In addition, add a LogConsole library class to simplify operations with the log console. This class can be quite simple; it's just an OutputStream subclass that implements the writeFromStream method to call logConsoleSay().

Add a TopicQualifierResolver subclass of Resolver, specifically for resolving qualifier phrases in topics. Use a new instance of this class instead of the direct object resolver in TopicActionBase. (The direct object resolver isn't really appropriate, and doesn't work at all with plain TopicActions, because they don't have direct object resolvers at all.)
Add a new mapping macro, iobjAsDobjFor(), that makes it safe to map an indirect object handler to a direct object handler in the same object. Use this for the mapPushTravelHandlers() macro.

(The mapPushTravelHandlers() macro essentially wants to decompose the two-object action into two single-object actions, which isn't safe with the regular asDobjFor() mapping. The difference with iobjAsDobjFor() is that this new routine temporarily makes the indirect object take on the direct object role, so that the target dobj handler sees the proper object in the direct object slot.)

Add a LabeledDial subclass of Dial. This class accepts arbitrary text labels as dial stops; the property validSettings contains a list of strings giving the valid dial stop labels.
Remove any .t and .tl extensions from adv3.tl and en_us.tl. (The extensions are implied by the file types; it makes the library file more portable to omit the extensions, since the compiler will automatically apply the default extensions using the appropriate local conventions when the extensions aren't explicitly included in the names as they appear in the library files.)
Get rid of Thing.articleIndef; games should simply override aName instead when they want to override the default indefinite article determination.
Change the way theDisambigName, aDisambigName, and countDisambigName work. If name == disambigName, then return the corresponding xxxName from xxxDisambigName; otherwise, apply the same algorithm to disambigName that the corresponding xxxName applies to name. For example, in aDisambigName, if name == disambigName, simply return aName; otherwise, apply the indefinite article algorithm to disambigName and return the result.

This change will allow disambigName to be overridden without requiring all of the xxxDisambigName's to be overridden at the same time, because the xxxDisambigName's will by default apply the standard algorithms to the modified disambigName. At the same time, though, this has the virtue of the original implementation that, in the common case where disambigName is not overridden, any overrides to theName, aName, and countName will be used for the corresponding xxxDisambigName's. Furthermore, since these are all still separate methods, objects can still separately override each xxxDisambigName as needed for cases where the disambigName is customized and the customized name requires overriding the normal article algorithms.

While we're at it, add pluralDisambigName, using the same logic.

Remove the dictionary properties possessiveAdj, possessiveNoun, and their corresponding grammar. (These are no longer needed, since we now have explicit grammar for all of the possessive pronouns in adjective and noun usages. At one time, these were used for adding vocabulary for the player character, but it worked better to use the grammar and resolver mechanism more explicitly.)
Rework "'s" handling for literal vocabulary:

First, remove the special-case tokenizer handling for "'s" words that appear in the dictionary, so that we handle all "'s" words uniformly in the tokenizer: all "'s" suffixes are treated as separate tokens.

Second, never add "'s" words to the dictionary in initializeVocab(). Instead, define a new dictionary property, adjApostS; when we see a "'s" word in a vocabulary list, remove the "'s" suffix and add only the prefix to the dictionary, but add it as an adjApostS instead of as an adjective. (For simplicity, don't bother with nounApostS at all; allow only adjectives as apostrophe-s words.)

Third, add a new grammar production, adjWord, that accepts either a single adjective or an adjApostS followed by an apostrophe-s token.

Fourth, where appropriate, change 'adjective->' grammar rules to use 'adjWord->' instead.

These changes correct the problem that adding a literal "'s" word to an object's vocabulary prevented that word from being used as a true possessive phrase in the grammar. The old tokenizer rule was that a word that appeared with an explicit "'s" suffix in the dictionary was kept intact, rather than split into separate tokens for the root word and the "'s" suffix. Since the word was kept intact as a single token, it couldn't match the grammar rules for possessive phrases, which only match the separated form. These changes allow the same word to be used in either context, since everything is treated the same way in the tokenizer; even if a word is used as a literal vocabulary word with a "'s", it'll still be split up during tokenization. Even with this change, we can still match explicit "'s" vocabulary words, thanks to the adjWord grammar.

Note this has one small potential impact on existing games: if an object explicitly defines its own 'adjective' property (rather than using the automatic library initialization), and a word defined in the adjective list ends in apostrophe-s, then that word must be stripped of the apostrophe-s suffix, removed from the 'adjective' list, and added to a new 'adjApostS' list instead. Similarly, if any vocabulary words that end in apostrophe-s are dynamically added with G_dict.addWord(), the apostrophe-s words should be stripped of the suffix and added under the '&adjApostS' property instead of the '&adjective' property.

Add noteLiteral(lst_.getOrigText()) to getVocabMatchList() in simpleNounPhrase(misc)? This would make the length of text in a miscellaneous word list a match selection criterion (less important than the presence of a misc word list). This won't change anything in cases where we want to choose between a match with a misc word list and one without, as the presence or absence of a misc word list is more important than literal length, but it will help distinguish between two matches that both have misc word lists. Since we prefer a shorter literal match, this will have the effect of preferring to match the more structured interpretation, since a shorter misc word match means a longer grammar match.
Fix run-time error with "a " (the 'inherited' argument list is wrong in AskAboutAction.getDefaultDobj; the same problem is in TellAboutAction).
Rename class Fixed to Fixture.
Add new class Immovable: this is for objects that aren't fixed in place obviously or by their very nature (as are Fixtures), but rather are fixed for some other reason: great weight, hidden fasteners, etc. This class differs from Fixture in that it disallows actions like take, move, push, and pull not in the verify() but in the action().

Add a subclass, Heavy, for objects that are immovable because they're very heavy. This is suitable for things like large furniture and big boulders.

Remove moveInto() override on Immovable. (This attempted to be a last resort to disallow actions that involved trying to move the object, but it prevented programmatic relocation as well, creating more problems than it solved.)
Fix problem with applying AGAIN to a command issued to another actor: the target actor becomes "busy" and won't accept another command until a turn passes. The problem is that we're not accounting for synchronous actors in the AGAIN processing: we need to tell the issuer to waitForIssuedCommand on the target actor.
Fix problem in GiveTo and ShowTo actions: getDefaultIobj calls inherited() with wrong argument list.
Fix the parameter list for npcActionMessages.okayTurnTo.
Make RandomTextList customizable with the percentage of the time a message is generated at all. Random message lists are often used for atmospheric messages, and it's usually better if these messages don't appear on every single turn. Add a 'messagePercent' property that determines the percentage of turns where a message is generated; the default is 100, which leaves the default behavior as it was (i.e., a message is generated on every turn).

In addition, especially with random atmospheric messages, it can get tedious to see the same messages over and over if you spend a lot of time in the same area. It's therefore often the case that we want frequent atmospheric messages when the player first gets to a location, but then we want the frequency to drop dramatically after the player has spent more than a few turns there. To make this easy to handle, add a couple of new properties: 'messageReduceAfter' is the number of times that we want to see messages at the initial frequency, and 'messageReduceTo' is a new value for 'messagePercent' that we'll apply after we've generated the messages 'messageReduceAfter' times. Make these nil by default, which means that there is never a reduction in the frequency. Authors can use these properties to generate random atmosphere messages at high frequency at first, but then drop the frequency to just an occasional message after the player has been in the location long enough to get the idea.

Add a new class, NominalPlatform. This is a "secret" object: it's not visible to the player as a separate simulation object, and won't normally have any vocabulary or appear in any room listings. The purpose of this object is to make it easier to describe NPC's as standing somewhere specific within a room; the author locates an NPC within the NominalPlatform, and the library will automatically describe the NPC as standing on the platform: "Bob is here, standing in the corner." The author can also customize the display methods for the nominal platform so that the description is something like "leaning against the lamppost."
Add a new property to Thing, specialContentsLister, that lets individual objects and rooms customize the lister they use to generate special descriptions. Set the property by default to specialDescLister.
Add a new topicPhrase grammar rule that matches a miscWordList without badness. (The normal topicPhrase grammar matches a singleNoun, which matches a miscWordList, but it does so with badness; we want to add an explicit rule that matches a miscWordList without badness.) It's in the nature of topic phrases to go outside of what's implemented in the simulation model; we don't want to treat resolvable and irresolvable topic phrases any differently at the grammatical level, because doing so can lead to inconsistencies when parsing a verb containing both a topic and another kind of noun phrase.
Add gLiteral to adv3.h, analogous to gTopic.
Change Thing.dobjFor(Attack) so that the default Attack handling is simply to display the message "it's useless to attack that." Remove the askIobjFor(AttackWith). Specifying a weapon shouldn't be required; especially for attacks like "hit" and "kick," asking for an indirect object will seem wrong to a player. This makes a tiny bit of extra work for authors when AttackWith is overridden, since Attack will have to be overridden as well in such cases, but this is probably worthwhile anyway because it'll encourage authors to make the same distinction between weapon and weaponless attacks that players are likely to make in their own minds.
Move the code from Contents.examineContainerContents into Thing.examineStatus (actually, into a new method called from Thing.examineStatus, call it examineListContents). Get rid of Surface.examineSurfaceContents. Ordinary Thing objects should traverse into their contents during Examine; this shouldn't be limited to Container and Surface objects. We need to do this listing in Thing, because a Thing could contain a component that's a container, and we want to list its contents when examining the Thing.
Fix bug in Action.getObjPreconditions: if pre is nil before appending the catch-all list, we'll get an error. Check that pre is nil before appending, and just use the catch-all list by itself if so.
Change the TravelConnector.describeDeparture() and describeArrival() interfaces to add a new parameter for the traveler. Use the traveler parameter instead of the global gActor. This makes the travel routines more flexible, and in particular allows them to be called from outside of commands (in daemons, for example). It also will improve the handling for non-actor travelers (vehicles in particular).
In RoomConnector.describeDeparture/Arrival, use gPlayerChar rather than gActor as the point-of-view object in directionForConnector(). The description is being generated for the benefit of the player, so it should be from the PC's perspective.

Split off a new method from each of Traveler.describeDeparture and describeArrival: describeNpcDeparture/Arrival, which is called when an NPC (or a traveler not involving the PC) is doing the travel. This will simplify overriding the messages for vehicles and other special travelers when desired, since the overriding method won't have to make all of the checks to see if it's the PC doing the travel.

Run all of the libMessages.sayDeparting/Arriving connector-specific messages through an intermediate method call in the Traveler; these new Traveler methods by default are simply be covers for calls to the libMessages methods. The purpose of the extra layer of calls through Traveler is to make it easier for individual Traveler subclasses to customize the messages on a per-connector-subclass basis.
'@script.txt' at the command prompt should start reading from the named script file. (This should work essentially the same way it did in tads 2, except that we want to handle this in the library instead of in the interpreter.) Also, '@@script.txt' should run the script in "quiet mode," which is to say that input and output generated while reading the script are to be suppressed.
Add some new classes to misc.t for "shuffled" random selections. A shuffled selection is a random selection taken from a set of values that doesn't repeat a selection until we've gone through all of the values once, like dealing from a shuffled deck of cards.

ShuffledList: the basic shuffled selection class. Keeps a list of values, and returns a randomly selected element on demand.

ShuffledIntegerList: a specialization of ShuffledList that returns a randomly selected integer from a given range.

ShuffledTextList: a subclass of RandomTextList that makes its selection using shuffling rather than independent random selection.

Add a point-of-view parameter to Thing.showSpecialDescWithInfo().
Add a parameter to the following library messages to indicate which object is being described (the object is needed for things like gender and number agreement in some languages):

openMsg, closedMsg, lockedMsg, unlockedMsg, onMsg, offMsg

Add a parameter to the following library messages to indicate which actor is being described (the actor is needed for things like gender and number agreement in some languages):

statusStanding, statusSitting, statusLying, statusStandingOn, statusSittingOn, statusLyingOn

Add a parameter to the various name methods in ThingState (listName, inventoryName, wornName) providing a list of the objects being shown in this state. This is needed for some languages so that the state description can agree (in gender and number, for example) with the objects being desribed as being in the state.
Change the count parameter to the full list in actorStandingGroupPrefix and the related methods in libMessages. This will allow the prefix/suffix messages to check any attributes of the individual objects in the list, such as gender mix, that might affect the message.
The INSTRUCTIONS command should not take any turns (make it a "system" command). Also, it should not consume any game time on the real-time clock (which, given the size of the instructions, could be noticeable).
Add an extended form of inputManager.getInputLine(), which takes a new InputDef object to define the input parameters. Use this new parameter definitions object to determine what to display to set and remove the input text style; do this instead of unconditionally using the <.inputline> style tag. The InputDef class should use <.inputline> as the default, of course. Keep the existing getInputLine() method, but make it a cover for a call to the new extended version, setting up an InputDef object representing the parameters.
Make "remove" (with a direct object but no indirect object) a fully separate verb from "doff". In English, "remove foo" usually means "doff foo", but it can also have the substantially separate sense of removing a component of an object. In the English library module, provide a default mapping of "remove" to "doff" in Thing, to keep the default meaning the same. Making a separate Remove action will allow this action to be independently overridden per object when it's desirable to do so.
By default, don't list anything in the credits for a library module. We should leave the formatting of the credits fully up to the author.
3.0.5
Add an optional mainRestore() function, to be provided by the game and called from the run-time startup code when a saved game is explicitly selected for restoration at startup (such as by double-clicking on a saved position file from a GUI desktop, or using the "-r" option with the command-line interpreter).
Change runGame() so that it no longer takes the initial player character parameter. Instead, callers should always set gPlayerChar explicitly prior to calling runGame(). (This change makes runGame() more sensible for cases where a game is restored: since the restore operation will restore gPlayerChar as part of restoring the rest of the game's state, it would be redundant to pass gPlayerChar as a parameter to runGame() only to have runGame() set gPlayerChar to that value.)
Fix the bug in EventManager.removeMatchingEvents ('eventMatches' should be 'cur.eventMatches')
Add a new output stream method, addOutputFilterBelow(), which adds an output filter at a given point in the filter stack.
Remove the "\( \)" escape sequences (for highlighted text). Authors should use HTML markups and instead.
Combine open/closed and contents status listings into a single message, to make the openable container default description less choppy.
Rename maxBulk to bulkCapacity, to make the meaning clearer. Similarly, rename maxWeight to weightCapacity.
>put trike in bag. drop bag. ride trike. (produces a run-time error) The problem is that we're failing to treat the bag as an invalid "staging location" on the way to boarding the trike. Add a routine, checkStagingLocation, to Thing, and call when moving an actor into a chosen staging location; this routine should generate an error explaining why the travel is impossible and terminate the command with 'exit'. BasicLocation should override this to allow being used as a staging location.
add default Thing handling for 'feel' and 'taste' commands
>close bag; x all in bag
red ball: You cannot see that. [etc]

We're incorrectly resolving the contents of an object for an "all in x" phrase, even when the contents can't be seen. We must filter the resolved objects to include only the visible contents.

"all in x" doesn't filter for objects hidden from "all" phrases; it should.
Standing should be a precondition of "jump".
The noMatchDisambig and disambigOrdinalOutOfRange messages should use <.parser> open tags, but not close tags: instead, the <.parser> mode should be left open, to be closed by the disambiguation re-prompt that always follows.

askDisambig should provide an open <.parser> tag if and only if askAgain is false: this will let the mode flow in from the preceding re-prompt message from noMatchDisambig or disambigOrdinalOutOfRange. askDisambig should always close the <.parser> tag.

Get rid of parserMessage(), and just use <.parser> tags instead.
We shouldn't be able to read at brightness 2. For Readable, differentiate readability according to brightness and transparency, as long as a readDesc is defined. (When no readDesc is defined for the object, use the default "examine" behavior as usual.)
Disambiguation: when indistinguishables are involved, answering with an ordinal applies the ordinal to the full list, rather than the reduced list offered:

>take coin
Which coin do you mean, a gold coin, a silver coin, or a copper coin?

>second
Taken. [actually takes a gold coin, since more than one was present]
Add a new option flag, libGlobal.allowYouMeMixing, that controls whether or not the parser accepts "you" and "me" in commands as equivalent. Set this to true by default, since most games will not have both first- and second-person narrative characters at the same time, and hence it seems unlikely that it will ever create confusion if the player can use "you" and "me" interchangeably. (Having the option to make the parser strict about this will take care of any cases where a game actually does have separate first- and second-person characters present simultaneously.)
The forwarding scheme isn't quite right. Replace it.

First, get rid of dobjForwardTo, iobjForwardTo, remapTIAction, dobjRemapTI, iobjRemapTI, dobjRemapTIReverse, and iobjRemapTIReverse.

Second, add a new mechanism that combines the old forwarding and remapping schemes into a single new system. Use syntax like so:

desk: Fixed
  dobjFor(Open) remapTo(Open, drawer)
  dobjFor(Close) remapTo(Close, drawer)
;

The first definition above maps the Open action, when applied to the desk, to a replacement action of Open applied to the drawer instead. The second does the same thing for Close. This replaces the old dobjForwardTo/iobjForwardTo scheme, so we no longer need forwarding at all. The difference with the new remapTo scheme is that we use a replacement action for the remapping - the remap for Open above is essentially like using replaceAction(Open, drawer) for the action definition of Open on the desk.

Further, consider this syntax:

lid: Fixed
  dobjFor(Push) remapTo(Open, jar)
  dobjFor(Pull) remapTo(Close, jar)
;

Here we've remapped not only the object (as we formerly did with forwarding), but also the action. The first definition above says to replace "push lid" with the new action "open jar."

Next, consider this:

ninjaThrowingStar: Weapon
  iobjFor(AttackWith) remapTo(ThrowAt, self, DirectObject)
;

In this case, we're doing what the old dobjRemapTIReverse did, but with considerably clearer syntax. We're saying that when we resolve the indirect object of "attack with" and find that it's the throwing star, we should remap the action to "throw at ", where is the original direct object of the AttackWith action. The important thing about using the "DirectObject" argument is that it tells us to use the pre-resolved match tree for the direct object if we haven't yet resolved the direct object noun phrase - this means that we'll be able to resolve that noun phrase in the context of the new action (ThrowAt) rather than in the original action (AttackWith) context.

Compiler: for each nested object definition, set a property of the nested object, 'lexicalParent', to point to the lexically enclosing object.
Change the default message for talking/asking/etc a random object to indicate more specifically that it's illogical to talk to the object.
Add a class for a complex container, for objects that contain objects in multiple ways. Examples: a crate that can contain things within and also have objects placed on top of it; a container with components.

The class should treat all of its immediate contents as components, so it should be based on Thing rather than Container or Surface. It should have two additional properties: one for a secret object that serves as the internal container, and another for a secret object that serves as the internal surface. Commands like "put in" and "look in" should be redirected to the internal container, if one is defined; "put on" and the like to the internal surface, if present. The contents lister must specifically show the contents of the secret internal container and surface as though they were contained directly by the outer object.

Change default Thing handling for Push, Pull, Move, MoveWith (dobj), and MoveTo (dobj), so that verification succeeds with a logicalRank of 50, and report that there is no effect in the action. (These actions all involve physical manipulations that a player might want to try with any object, so it's better to allow these to pass verification, so that preconditions can be allowed to proceed rather than halting before even getting to preconditions.)
OutOfReach seems to put the object itself (not just its contents) out of reach. Add a separate test for reaching 'self'.
In Actor.canReach, don't make touching our immediate location a special case - just traverse the sense path to the container as normal.
>look in stove
(First opening the stove)
Opening the stove reveals a loaf of bread.  The stove contains a loaf of bread.

It would be good to remove this redundancy: if we're opening something implicitly for a look-in command, we shouldn't bother showing what the opening reveals. (We can handle this in Openable's Open action handler: if the action is implied, and the parent action is LookIn with the same direct object, we can suppress the revelation message.)

Suppress default "you see nothing special about it" or "it's an ordinary x" descriptions when there's a special status message, such as contents to be listed.
>look in small alcove
You see nothing in it.  A trophy is visible...

(Perhaps if we special description items, and nothing listed, we should display no message at all.)

Use tags for line-input modes in commandSequencer, eliminating method calls. This should remove the fragile order-of-call issues that we currently have.
Should we reformat some messages involving implied commands? For example:

>put coin in bag
(first opening the bag)
Opening the bag reveals a keyring. Done.

Perhaps we should put that "done" on a new line - no paragraph separator, just a new line. (In general, perhaps when there's a non-default response from an implied command, we should add an "implied command terminator separator," which by default would simply be a newline.)

Rename the English-specific part of the library to make it easier to add new languages in the future. In particular:
  • Create a new US English subdirectory of the adv3 library directory; use ISO 639/3166 codes for the naming convention, so the US English directory is called en_us.
  • Move us_eng.t to en_us/en_us.t
  • Move us_eng.h to en_us/en_us.h
  • Move msg_neu.t to en_us/msg_neu.t
  • Add a new library, en_us/en_us.tl. Move the English-specific source file inclusions out of adv3.tl and into en_us.tl.
  • Include the new en_us.tl library in the auto-generated makefile for the new-project wizard in Workbench. (We'll have to add a user preference at some point that lets the user select the language library to use, rather than using en_us.tl unconditionally.)
Add a general IF instructions module (like instruct.t from tads 2)
Fix nil dereference in transcript processing for ambiguous noun phrase errors.
Fix command state management for suffixes so that suffixes are always generated before the prompt. (We simply need to display <.commandbegin> in intput.t before displaying the prompt.)
Add a new command state, stateNoCommand, for interactive input. In this state, don't add any prefixes/separators/suffixes when reading input. This should be used for yes/no answers, answers to end-of-game prompts, and so forth - for anything where we need to read input interactively in the course of a command.
Move en_us.t Thing vocabulary initialization code to VocabObject.
For commands like "put coin with jar," we're asking "what do you want to put," which is a weird question. (This happens because we interpret this as "put coin with jar in missing-np" and then resolve the iobj first; this asks the usual question, which comes out the way it does because the dobj tentatively resolves to an empty list since it is a non-matching phrase. To solve this, we could use a dummy entry in the tentative resolution list, to signal that we actually have a dobj phrase, even if it's not resolvable to anything.)
It might be nice to catch constructs like this:

>open door with key
You see no door with key here.

In particular, it might be nice to recognize "<nounPhrase> <prep> <nounPhrase>" constructs specifically, and flag them as "command not understood" errors instead of "object not seen" errors. This could be handled by adding a "badness" production for np-prep-np structures for the prepositions commonly used in command phrases; by making this a badness match, we'll match it as a last resort when we can't find a valid verb phrase structure.

Add an extra category of reports, "extraReport", that's used to add extra information that doesn't replace any default report. Use this for the key-found-on-keyring report, since this report is supplemental to any default Locked/Unlocked report and shouldn't suppress it.
Move the call to initializeVocab out of Thing.initializeThing, and instead call initializeVocab directly from adv3LibPreinit.execute() for each VocabObject. (This will initialize vocabulary for non-Thing VocabObjects, such as Topic objects.)
Add a new inputManager method, promptForMore(), that shows the MORE prompt, taking care of flushing the transcript and dealing with the real-time clock. The method should take an argument specifying whether or not to freeze the real-time clock; but even if the clock keeps running, real-time events shouldn't be allowed to occur until after the user acknowledges the MORE prompt, because otherwise we'd defeat the purpose of the MORE pause, which is to wait for user acknowledgment before showing any new output.
Change the Decoration class so that its generic not-important message can be overridden simply by changing a property, rather than having to override all of the Default handlers individually.

Likewise for Distant and Intangible.

Add a new 'isTopLevel' property, which determines if an object is a top-level container within the game world. Set this to nil by default for everything except Room objects, where we set it to true. Use this property in evaluating isIn(nil): for an object with a nil location, isIn(nil) returns true if and only if the object is NOT top-level. This allows isIn(nil) to be used to sense whether or not the object is part of the game world, recursively considering the status of the object's containers.
Remove selfInReach from OutOfReach.
Fix problem with TravelConnector.verifyTravel(): when this routine is called, gVerifyResults is not reliably set to a valid list. In particular, gVerifyResults must be set to a valid result list from TravelAction.verifyAction().
Turn on the sense cache while generating the status line. Displaying the status line can involve some substantial sense calculations, so enabling caching while generating it can improve overall response time noticeably.
Fix 'follow wall' (generates nil pointer error)
Fix 'throw x at wall' (or floor or other room parts - nil pointer error)
Fix nil pointer error when addressing an ambiguous target actor.
>sit on darker chair
>sit on chair
Which one?
>darker

This is allowed, but shouldn't be - the reply should be "you're already sitting on the darker chair." (The problem is that we verify the lighter chair as okay, and we remember that we verified something as okay but don't bother remembering what; we need to track which objects we verify as okay individually.)

Make searching a surface (or looking in a surface) show the surface's contents.
Check bulk when adding a new item to a surface. (This will fix the weird inconsistency with chairs that allows you to put an object on a chair you're sitting on, even if there's not room for you to sit on the chair when the object is already on it.)
Reduce the verification logical rank of non-Food objects for >TASTE.
>sit on chair
>look
... You contain a keyring ...

(We shouldn't mention your contents in that manner. The problem is that Actor inherits contentsListed from Fixed; Actor should override contentsListed and set it to nil, since an actor's contents shouldn't be listed in the ordinary fashion in a room description.)

Show object contents for EXAMINE and LOOK IN to full depth.
Searching (or looking in) a room part shows contents as being "in" it, which isn't appropriate for walls, ceilings, or floors.
For THROW AT FLOOR, perhaps we should have a message other than "Dropped" - something indicating the action is more violent than just setting the object down.
Make contents listings go to arbitrary depth. Rather than showing a separate list for the contents of top-level list items, show the contents of each item parenthetically, and then recursively add these parenthetical lists to show all contents. (Only do this when the ListRecurse flag is set.)

Add a new Thing property, contentsListedSeparately, that allows an object to control whether its contents are listed in the new in-line style, or as the traditional separate list. Make this nil by default.

For "tall" listings, show contents of unlisted top-level items in separate lists after the main list, just as we do for "wide" listings.
Remove the need to call showContentsList() separately after showList(); instead, call this automatically from showList() if the ListRecurse flag is set.
Throwing destinations aren't working quite right. Refactor things a bit: rename getFallDestination() to getHitFallDestination(), and give it the previous container in the path as a parameter, so that it can decide whether the object is being thrown from inside or outside (or, in the case of a multi-location item, the source location). Make things drop to the floor in most cases; games can override to make things fall into intermediate containers when desired.
Change Actor.standUp to use reportFailure() when already standing.

>out
Out of what?
>e
You can't get out of that (the east wall).

It would be nice to treat such responses as new commands. We could probably adopt the heuristic that we give the new command interpretation priority over the noun phrase interpretation of a reply to this prompt. Anything that looks like both a syntactically valid noun phrase and a syntactically valid command probably matches the noun phrase syntax only because it's extremely abbreviated; the chances of the verb phrase match being an accident are somewhat less. In addition, it should be less confusing to the user to treat the response as a new command when a noun phrase was intended than vice versa, and it should be fairly obvious how to force a noun phrase interpretation (adding "the" would do the trick, for example).

Can we go to arbitrary depth for contentsListedSeparately? In particular:

  >i
  You are carrying a duffel bag (which contains a pen cup).  The 
  pen cup contains four pens.

Right now, we're not adding that bit about the pens, because we think we're done with the top-level list, as we've already listed everything: we scan the duffel, and decide that we don't need to recurse into it, since it's listed and has in-line listed contents. What we'd need to do is add another recursive descent to look for everything that we listed at the second (or deeper) level that has contentsListedSeparately.

Tweak the parser's command match-tree ranking mechanism a little, so that differences in the number of occurrences of a problem are counted in a second pass, after we've exhausted the possibility of any differences in the presence or absence of problems. This will make slightly better choices in certain cases, such as when we have two separate noun phrases in the action, and one interpretation treats only one as ending in an adjective while the other has an adjective ending in both phrases.
Add a profiling facility, to gather statistics for performance optimizations in the library.
Rename senseInfoList to senseInfoTable, and make it return a LookupTable rather than a list. Sense information lists are almost always used as random-access tables, so we can speed up some operations by representing these as lookup tables keyed on object.
Rename connectionList to connectionTable, and make it return a LookupTable rather than a list. Since we want only one copy of each object in the result, it's faster to build this as a lookup table, where we can determine whether or not an object is already present much more quickly than we can with a vector.
>e
>put cup in desk
>undo

(Takes back turns through "e". The problem is that we're marking a remapped action as a nested action; it's not really nested, because the original action is completely replaced and will never reach the execution phase. Do not mark remapped actions as nested.)

Add Thing.specialDescOrder to control specialDesc the relative order of specialDesc listings.
Change the format of the first-score-notification supplemental message, which explains how to turn off score notifications: rather than showing it as part of the same paragraph with the score, start a new line and show it as a separate notification message.
Rename UnqualifiedPluralProd to DefinitePluralProd. In the English parser, base implicitDetPluralOnlyNounPhrase(main) and explicitDetPluralNounPhrase(definite) on DefinitePluralProd, since "books" and "the books" should act the same way in English.
Simplify the Key and Keyring Attach and Detach handling by using remapTo() in the keyring: remap "attach x to keyring" to "put x on keyring" in Keyring.iobjFor(AttachTo), and remap "detach x from keyring" to "take x from keyring" in Keyring.iobjFor(DetachFrom). This removes a bunch of code from Key to handle the remapping at its end - that code all predated the remap mechanism, and can now be greatly simplified by using the new mechanism.
Fix a problem in vocabulary initialization: if a hyphen is used as a placeholder in a vocabWords_ setting, the hyphen is added to the object's part-of-speech property (it's not added to the dictionary, but it does show up in the list for the part-of-speech property). It should be omitted from the list, since it's not really a vocabulary word.
Fix finishOptionUndo so that it can be used in daemons. This requires a couple of changes. First, remove the transcript flush - this is a relic of the pre-'transient' transcript mechanism and is no longer needed. Second, daemons and fuses need to be able to use TerminateCommandException and the like to exit, so we should catch command-ending exceptions in the event manager.
Fix examining sightSize=large objects at a distance. (The problem is that Thing.canDetailsBeSensed() is attenuating the ambient light level for the sense path, which is unnecessary as the SenseInfo already does this. canDetailsBeSensed doesn't need to go to so much trouble - any SenseInfo with trans != opaque indicates the object can be sensed, since trans will always be set to opaque for objects that cannot be sensed.)
Add tok.t to system.tl. (This file isn't actually necessary to compile a simple program, which is why it hasn't been in system.tl all along; but it is necessary for any game based on the library, so on balance it's probably better to include it in the default library.
Separate some action-time checks into check() routines: Container.dobjFor(LookIn); Dial.dobjFor(TurnTo) (this could perhaps benefit from some 'verify' checking of the literal value as well).
Add a specialDescLister, and use it for listing special descriptions. Add a new property of Thing, specialDescListWith, which returns a list group to use when showing special descriptions; use this new list group generator in specialDescLister. This will allow special descriptions to be grouped when desired, simply by using the standard list grouping mechanism.
Add an "isCircularPassage" property to TravelConnector: when this property of the connector is true, fully describe travel that winds up in the origin. (We normally don't bother describing such circular trips, but sometimes it's desirable to be able to do so.)
In en_us.t, add askDobjResponseProd=singleNoun for each action that takes a singleDobj as its direct object.
In en_us.t, remove the redundant syntax for "walk in/into" and "go in/into" from VerbRule(GoThrough), as these are already handled by VerbRule(Enter).
In en_us.t, use tags wherever literal quotes are used, rather than ASCII-34 quotes.
Convert the parser to use the new Dictionary API. In the English parser, install a StringComparator to perform case folding and truncation matching. Remove case conversions from the English tokenizer, as they're not needed with the StringComparator.
Refactor the travel connector operations so that everything turns into a TravelVia on its connector. Handle direction commands ("go north") by using replaceAction(TravelVia, connector). This simplifies the travel verbs, and makes the task of defining travel connectors more consistent with defining other action handlers.
Move the travel connector checks for dark-to-dark travel and for travel barriers into the TravelVia check() handler.
Rearrange the room description code (lookAroundPov, etc) so that lookAround can be called on any object. To do this, move all of the room description mechanism from BasicLocation into Thing, and work the NestedRoom code into the Thing code for descriptions.
Add a return value to basicEventManager.removeMatchingEvents, indicating whether or not a matching event was actually found.
Fix getTenativeLiteralText argument mismatches in parser.t when calling getLiteralText.
In MessageBuilder.generateMessage(), for the case where we're substituting '{actor}' but there's no current action, use the current gActor value if it's not nil, or gPlayerChar if gActor is nil (rather than always using gPlayerChar - using gActor instead allows a daemon or other non-action message producer to set gActor to indicate the actor doing whatever it is that's generating the message).
In Actor.addPendingAction() and addFirstPendingAction(), take a resolved object list as the last varargs-list argument, and pass it to the PendingCommandInfo constructor.
Hide Fixed objects from "drop all".
Add some more default verbs: CutWith, Kiss.
Add RoomPart.isIn(). Consider a RoomPart to be within a given location if its room part location is equal to or within the given location, or the inherited isIn returns true.
Add some more string-quoting styles to the English tokenizer: `like this', and with typographical ("curly") single and double quotes (\u2018 and \u2019 for single quotes, \u201c and \u201d for double).
Add a quotedStringPhrase production type, for rules that explicitly call for quoted strings (but not unquoted literal text). Use this production to build the quoted string rule for literalPhrase.
Add SAVE, RESTORE, and SCRIPT command variations that take quoted strings giving the filenames to use.
Traveler.travelTo in a daemon dereferences gAction - check to see if gAction is nil, and skip that step if so.
Change all uses of 'seen' to use the seenBy() and setSeenBy() methods. Move these from BasicLocation into Thing so that all objects uses them uniformly.
Bug: TERSE mode doesn't show full descriptions on first entry.
Some of the objects shown in the introductory room description aren't getting marked as seen. (The problem is that Thing.lookAroundWithin is using gActor in a few places where it should be using the 'actor' parameter.)
Bug: NoTravelMessage is commented as being an apparent connector, but is implemented as a non-apparent connector. Leave the implementation the way it is, but fix the comments, and add a new class that acts the same as NoTravelMessage but has an apparent connector (call it FakeConnector, perhaps).
Rename LiteralAction to LiteralTAction. Add a new LiteralAction class that takes only a literal phrase as its object (i.e., no other direct object), for commands like "say random stuff". Note that DefineLiteralAction is likewise renamed to DefineLiteralTAction, and we add a new DefineLiteralAction for defining an action with only a literal phrase for its object.
Add a PostUndoObject class, analogous to PostRestoreObject.
Reduce parse rankings for pronoun phrases vs noun phrases: if a word is explicitly defined as noun, it probably should be a stronger match than a pronoun interpretation of the same word.
3.0.4 and earlier
Consider this command:

>put ball in asdf
Which ball do you mean, the red ball, or the green ball?

>red
The story doesn't know the word 'asdf'.

>oops box
Which ball do you mean, the red ball, or the green ball?

The issue is that OOPS retries the entire command with an updated token list, and as a result, the disambiguation work from the first attempt at resolving the objects is lost for the second iteration. The reparsing is necessary, since the corrected typo could change the entire meaning of the command.

One possible solution: add a resolution pass specifically to catch unknown words. Don't do any other resolution on this pass - just deal with unknown words. This would ensure that we process OOPS before we ask any disambiguation questions.

Another possibility: keep a set of answers to questions, and the conditions under which the questions were asked. Clear the list each time we start a new parsing from scratch, but let the list survive if we reparse a token list. Whenever we're about to ask a question, look to see if the same question has been asked with the same conditions, and if so, reuse the same response.

All
Missing noun phrases: prompt for input when PC gave command
Missing noun phrases: use a default when possible
Object announcements: include a preposition when appropriate (announcing indirect object, announcing direct object with verb taking a preposition: ">dig \n (in the dirt)")
Pronouns
Again
Again: should we use disambiguation information from past commands? For example:

>take book Which book do you mean, the red one, the blue one, or the green one? >blue Taken. >g

Should this 'take blue book' or should it ask... Which book do you mean, the blue one, or the green one?

Right now we use the literal tokens from the original command, so we ask the disambiguation question again. This is exactly what we want in cases with indistinguishables:

You see five silver coins here.
>take coin
Taken.
>g
Taken.

But in cases where we have distinguishable objects, we probably want the resolved objects, not the words.

We probably want to distinguish this way: if we had indefinites or indistinguishables, resolve again from the original text. Otherwise, use the original resolution.

disambiguation: we're not keeping the full list properly in cases of indistinguishables:

>take coin
Which coin, a gold coin, or a silver coin?

>coin
Which coin, the silver coin, or the gold coin?

The second time around, the cardinality of the full list should not change, so we should be asking the same question as the first time.

Preconditions
We need 'touchDobj' and 'touchIobj' preconditions - use these for all objects that require that the object be physically manipulated by the actor but not necessarily held. These are required to deal with things like the contents of a closed transparent container, where an object can be in scope but cannot be manipulated.

As part of this, we need to find a way to say what object blocks a sense path opaquely, so that we can say why we can't touch something that we can see. There are two main cases: containers are in the way, and connectors are in the way.

Bag of holding: when an actor's hands are full and the actor is trying to take something, pick a held object and try to find a bag of holding for it. First, check the object's preferredHolder property - this gives an object or class that the object prefers as its bag of holding. If the actor is holding such a holder, move the object to the holder. If we can't find any affinity, look for any object the actor is holding of the generic BagOfHolding class.

Examples of specialized bags of holding: wallet, key ring, purse, duffel bag.

Formatting: where does the blank line come from before an implicit message for a single command?
use disambiguation filter even for indefinites, so we pick the best possibility if there are objects of different likelihoods
"The red one" should work for exclusion lists. It should probably also work in general - although 'one' should bind more tightly to an actual name, in the absence of a name it should serve as a generic noun.
"drop coins" - should we consider only the ones we're carrying? In other words, should we run verification and include only the logical ones? Probably - if there are some logical and some not logical, apply the command only to the logical ones; otherwise, apply the command to all of them.
"take both coins" - really wacky
"take two gold" should work (i.e., quantified ending in adjective)
consider:

>take both coins
Which two (of five gold coins or three silver coins) do you mean?

>two gold
You don't see that many coins here.

The problem is that we disambiguate with the reduced match list.

>take coin
Which coin, gold, silver, or copper?

>all but copper

no worky - might even be a gram prod bug, since we seem to have a weird firstToken/lastToken value in these cases

"take books except red" - disambiguation of the exclusion list should be limited in scope to the resolved plural list.
"take books except silver" - "You see no silver here" - this should ignore the missing object.
"take coins except copper" - the 'copper' should implicitly be plural rather than indefinite so that we skip all of the copper coins if there are several
>take gold coin, gold coin
gold coin: Taken.
gold coin: You're already carrying the gold coin.

When we have multiple equivalent items in scope, and we mention several items in a list, we should pick separate equivalent instances for each mention.

This should apply to disambiguation responses, too:

>take coin
Which coin do you mean, a gold coin or a silver coin?

>gold, gold
"take any ball except green" - takes the green if there's nothing else left
add verification for pre-conditions - objHeld, for example, would boost the likelihood for a held object
add the new pre-execution check as a separate phase (checkXxx?)
output capturers - one at a time
add object affinities for bag of holding
key ring - not just a bag of holding, but automatically adds items as they're picked up when the key ring is in inventory
Key ring messages: we should check for a non-default report, and if there is no non-default report we should include more details: "You take the key and put it on the keyring."
Probably should have an objDirectlyHeld precondition. This one would be used for commands that physically move an object; objHeld would continue to be used for commands that merely need to do something with an object. So, "put x in y" would require that x is directly held, while "unlock x with y" would only require that y be nominally held. For objDirectlyHeld, we'll make the implicit command silent if the object is being indirectly carried, so that we don't get weird things like this:

>i
You are carrying a paper bag.  The paper bag contains a key.

>drop key
(First taking the key)
Dropped.
If there's no doXxx, fail verification with a generic Illogical('you can\'t do that').
For travel: shouldn't we have a verify phase for moving the actor?
need to export propNotDefined in a system file
take key: if it started on the keyring, we should take it off the keyring
Travel connectors
Fix 'i tall' listings to show contents with a better format
Move senses to a separate module.
>get coin
Which coin, a gold coin, a copper coin, or a silver coin?

>coin
Which coin, a copper coin, a silver coin, or a gold coin?

The re-ordering on the second round is weird - we should keep the order stable if we can.

>get ball
Which ball, red or green?

>blue
You don't see that here.

The error shouldn't be "you don't see that here" - it should be more like "that's not one of the choices you were offered." However, it would be better to widen the scope again to the real scope, rather than failing.

Add maxWeight and weight enforcement
Move pre-conditions to the objects. Keep the verb preconditions as well, but verb-specific pre-conditions should never mention the objects.
Mark objects for which we've displayed descriptions, for topic scoping
Add "module ID" objects, for library extensions. The "credits" command should run through the module ID's and show a credit for each one.
Make styles into objects
Fuses and daemons
Need to apply logical tests again after running preconditions. (Try this: "put rusty key on keyring" - it'll pick up the key for the 'held' precondition, which will put the key on the keyring, but then the explicit put-on-keyring command will succeed, which it shouldn't.)
Generalize the showList mechanism to allow for other types of hierarchies besides contents to be shown. Use the lister interface to virtualize the hierarchy traversal.
Use the generic showList mechanism for the full score lister, rather than using a specialized lister that does essentially the same thing.
Score change notifications: add daemon that senses score changes and notifies between turns.
Do not count the time taken for implicit commands.
Add a "message generation context" that determines what is generating messages. This object must be sensible to the player character for messages to be generated. For convenience, define a callWithMessageContext function that establishes the context, invokes a callback, then restores the enclosing context.

When establishing a new context, we check to see if the generator is in scope in the given sense. If not, we hide messages; if so, we show messages.

A nested context should actually be considered a context switch, because we don't want an enclosing hiding context to hide an enclosed showing context. For example, if we are generating messages with sight scope for Lloyd, and we have a nested context where Lloyd is saying something and so we now are in hearing scope for Lloyd, we want the nested context to show its messages if in fact Lloyd is in hearing scope even if Lloyd is not in sight scope (maybe the player character and Lloyd are in contact over a walkie-talkie).

NPC messages: only show if NPC is in sight of PC (using message generation context)
Use message generation context for fuses and daemons. Allow events to be created with a context object and sense object; whenever an event is executed, establish the context for the duration of the event's execution.
try this: bob, n. z. s. / n. z

We should see bob leaving on his third queued move, but we don't for some reason.

Do we really want to remove the objects directly involved in a command from the beforeAction and afterAction processing? It seems like it's an unnecessary inconsistency.
Should consider directing report messages to the pc/npc message objects. This would more readily allow separate messages for reporting the results of pc and npc commands (for example, "Taken" vs "Bob takes the book"). This is probably trivial, since we already have the pc/npc message generator division anyway; we probably just need to use it rather than verbMessages for generating report messages. (If there's really a good reason to have a separate verbMessage object, maybe we should have a per-actor verbMessages object, the way we do with the default library messages generator.)
Missing blank line between commands on parser failures:

>bob, n. get silver coin.
>n
In room descriptions, list actors separately.
Don't let the room lister include actor inventory listings with the main room contents listing.
Footnote system
Add an initExamineDesc, to parallel initDesc when an object is in its initial position and is explicitly examined.
Check changes in object bulks for effects on containers. If a container doesn't allow a change in child bulk, fail the command that attempted the change. (For example, in a stretchy bag, putting a new object into the bag should be disallowed if doing so would make expand the bag so much that the bag would be too large for its own container.)
Stretchy bags, that conform to their contents (in the sense that the bulk changes according to its contents)
Use normal command result reports (especially ReportFailure) in precondition checks.
For target of "put in", elevate likelihood for an item being held.
implement save/restore
implement restart
implement undo
For undoing commands to NPC's, show the NPC as part of the command being undone: "undoing one command: bob, go north".
Add '\{' and '\}' (push/pop formatter state) output sequences.
Implicit commands should not set antecedents.
"Take All" should include the non-fixed contents of fixed containers and surfaces within the room.
Status line
Footnotes: add FOOTNOTE MEDIUM mode, which shows new footnote references but hides references to footnotes that have already been read. This helps cut down on unnecessary clutter, since a footnote reference is unlikely to be of much interest once it's been read, assuming that footnotes are always extra information that isn't required to finish the game.
Weird:

>get large red ball, small red ball and drop them
[works]

>get asdf red ball, small red ball and drop them
Don't know the word 'asdf'

>oops large
...you see no drop them here

Why does it pick out the right interpretation normally but can't on a token reparse?

Add an "unobvious" verification mode that allows the command but will not be accepted as a default.
Decorations
Add dobjCheck, iobjCheck, dobjGen, iobjGen equivalents
Add a default mechanism for listing the inventory of an actor as part of the actor's description. ("Bob is carrying...wearing...").
Add a sorter method to the list group object, for simple control over the sorting order
Add input font switch when reading a command
When defaulting a direct object of a two-object command, include the verb's participle form in the default message:

>ask about watch
(asking Bob)

Most default messages are meant to be tagged onto the end of the command line, but when we're adding a direct object to a two-object verb, the added object goes in the middle of the command and hence the default phrase doesn't sound right when tagged onto the end of the command line. By adding the participle form of the verb, we make it clear that the default message stands alone as a new sentence (or sentence fragment, anyway) and is not meant as a missing continuation of the original command text.

"show iron key" - since the missing phrase form of this command has badness, this ends up being interpreted as "show key to iron", which is silly. For command forms like this with two objects and no preposition, we should probably add a non-badness single-object grammar that asks for the missing prepositional phrase. How we select the misisng-object phrasing over the two-object interpretation is unclear, though.
Parameterize the amount of time it takes for an actor to give an order to another actor.
try 'inflate raft and put it in bottle' - we don't get a blank line before the 'put it in bottle' response, presumably because it has an implicit command first
Floor/ceiling/walls for indoor rooms, ground/sky for outdoor rooms
Traverse the sense path for throwing an object at another object, and make the projectile land in the appropriate place when an intervening object prevents the throw from finishing.
Keep track of multiple logical results, and make comparisons based on the whole list. Distinguish different reasons (using 'key' values) for different logical results with the same ranking.

The idea is that if we have two objects that are both ranked the same way on one axis but are distinguishable on another axis, we want to consider the axis on which they're distinguishable. For example, if we type "put silver coin in jar," and one jar is open and the other is closed, we want to pick the one that's open. At the same time, they'll both be ranked as equivalently logical on the basis that neither is held by the actor. We don't want the being-held status to override the open status, but we likewise don't want the open status to override the being-held status if both are open but only one is being held. The solution is to ignore the equivalent being-held status when both objects have this same value, and to ignore the equivalent open status when both have the same value, but still consider these attributes when they differ.

Provide suitable default implementations for about 1.0e+6 verbs
"re-route" syntax - see, for example, keyring.iobjFor(AttachTo). Use asDobjFor and asIobjFor:

   iobjFor(AttachTo) asIobjFor(PutOn)
For travel: actorStanding pre-condition that automatically extricates the actor from chairs and the like. We must catch dangerous conditions in the verifier.
For missing iobj's, allow preposition in response:

>dig in dirt
What do you want to dig in it with?

>with shovel
Type/enter string/number on object
Type/turn to unquoted string?
In a two-object action, when assuming one object or the other, and then asking for the other, we should show the defaulted one before prompting:

>ask
(Bob)
What do you want to ask him about?
For disambiguation, never pick a 'not obvious' over an illogical, because a 'not obvious' essentially counts as an illogical for the purposes of picking objects.
It might be good to eliminate the redundant default announcement in cases like this:

>ask
(asking Bob)
What do you want to ask him about?

>watch
(asking Bob)
It might be good to eliminate the participle when asking for a direct object of a two-object verb and the indirect object is not yet known:

>ask
(asking Bob)

It would be better as simply

>ask
(Bob)
Turn x to string/number
Dials (turn, turn to)
Buttons (push)
Switches (turn on, turn off)
Levers (pull, push, move)
Doors
In the dark: should be able to list items you're holding by touch.
In the dark, 'look at' should indicate that it's the darkness that prevents an in-scope item from being inspected.
In the dark, inventory should do something better than say "you're empty-handed".
Climbables (such as stairs). These should probably just be travel connectors that go up and down.
Check scope for preconditions - make sure we don't apply a command to an object not in scope by way of a precondition implicit command. For example, if you type "go north" in the dark, and there's a closed door that you can't see, you shouldn't be able to open the door by way of the travel precondition.
>go through passage
>again

This somehow keeps the old passage in scope. Must need to check scope again for 'again'.

(The problem is that the last action is hanging on to its cached resolver scope lists. We simply need to drop the old resolvers so that we start the new command with new resolvers.)

climb up/climb down verbs
make sure we have makeOpen, makeClosed, makeLocked, makeUnlocked, etc. - in other words, encapsulate these types of state changes the same way moveInto encapsulates containment changes
Add an "inaccessible" verification failure - this would be more disapproving than any "illogical" level and would be used for "it's too dark" and "you can't reach that" types of failures.
The pre-condition execution sequence should probably be like this:

  for (pass = 1 ; pass <= 2 ; ++pass)
    run all verifiers
    for each precondition
      run this precondition, allowing implicit commands on pass 1 ONLY
    if no implicit commands were executed
      break

This would ensure that side effects of an implicit command are re-tested through previous preconditions. Each precondition would only be allowed to run its implicit command on pass 1 because this would prevent infinite loops from conflicting preconditions - on pass 2, if the condition isn't met by now, we'll assume it's not going to be, so we'll simply fail the command.

"actor, give x to me" should be handled as though it were "ask actor for x", via a replacement command.
actors: shouldn't allow taking possessions in general
Dark rooms: limit travel to adjacent lit locations?
Explicitly inherit base class vocabWords_ in initializeVocab() in us_eng.t.
touch/feel verbs
Add verb "listen" (both transitive and intransitive)
Add verb "smell" (transitive and intransitive)
Enterables
keys and doors: add options for boosting likelihood for keys:
  • on the first successful use of a key, automatically boost the likelihood for future disambiguation using the key if an option is set (this might be the default option)
  • if the author wants the player character to know from the outset which key goes with a door, the author can add the key to the lockable's knownKeyList - this can be useful for games with obvious key associations or key associations known from the outset to the player character (for example, if the PC has a key to their own house, the ought to know what the key opens)
locks and keys
lockable doors
Automatic key rings for unlocking doors - an "unlock" command with no indirect object automatically searches for a keyring in the actor's inventory, and automatically searches for a key on the keyring.
keyring: need to implicitly take keyring if not held when trying it
keyrings: message on success should be something like:

(trying each key on the keyring, you find that the iron key unlocks the door)

keyring search: when we already know the correct key, we should just default it rather than search the keyring for it
implicit commands that use 'replace' should inherit original implicit status (try 's' from the stair bottom - we get an "unlocked" report for the replaced 'unlock door' command)
For Fixed, indirect the illogical messages through properties, to allow easier customization.
Whenever any message appears before a room description in a command sequence, show a blank line before the room description. This applies especially to travel, but is generally desirable for any command that shows a room description.
Put iron key in duffel; unlock iron door - won't default the iron key, because the keyring takes precedence by virtue of being held.

The problem is that the objHeld precondition is applying a likelihood of 80 because the iron key isn't held, and the held keys are getting 100's. Maybe we need to reduce the likelihood of any other keys or keyrings when the known key is present, but this seems like a poor special-case hack for a problem that is likely to arise in other contexts.

To solve this, add a 'priority' to logical results. Use low priority for precondition rankings, because these are inherently of lower importance than unique attributes of the objects themselves.

Add an 'exit only passage' to be used as the receiving end of things like trap doors, chutes, and so on.
Message travel connectors: simple connector that shows a message as the connector is traversed.
preserve the case of tokens in literal phrases
Light/dark changes should be noted at the end of each turn.
Light/dark changes should be noted before/after each daemon is dispatched.
>move me to
What do you want to move it to?

Could we substitute "yourself" for "it" somehow?

Trap doors - in particular, the arrival message should indicate that the trap door automatically slams shut after the actor arrives.
Possessives - "take his box", "take bob's box"
possessives - problem with disambig:

>get key's jar
Which key's do you mean...

That should just be 'which key do you mean', not 'which KEY'S'

possessives - plurals
possessives - five of bob's keys
possessives - bob's keys except the green one
get all of bob's keys except the rusty and iron ones - even when this is just one key, we should announce it as though it were a multiple object (so we need to set some flag)
possessives:

>bob's
unknown word ''s'
possessives - problem with disambig:

>get key
Which key do you mean...

>get bob's key
The word ''s' is not necessary...

The problem would seem to be that we don't handle 's in disambig responses and treat it as an unknown word.

>look under me
You see nothing unusual under you.

We should make that second 'you' into 'yourself' instead. In general, can we change an objective case use of an object already used in the nominative should to a reflexive instead?

possessives - disambig response should allow 'bob's' as a choice, even if it's not offered
possessives - "get keys except bob's" - need special grammar for just a possessive in this context
>bob, x key
Which key?

>quit
This command cannot be directed to another character...
Distant items
Finish topic-verb implementation
Consider:

>lock door
(first closing the iron door)
(with the iron key)
Locked.

The defaulted indirect object is misleading. This should instead be:

>lock door
(with the iron key)
(first closing the iron door)
Locked.

The problem comes from the fact that "Lock" has an objClosed precondition for keyed lockable; "Lock" doesn't need this precondition, because "LockWith" has it. Removing the precondition will get the ordering right.

truncated adjectives seem worse than unknowns in ranking:

>type hello on typewri
do not filter the 'all' list with hideFromAll when looking for default objects
>get iron key
You take the key and attach it to the keyring

>get keys
brass key: You take and attach...
rusty key: You take and attach...
iron key: Taken.

It's irritating that the last one is taken off the keyring. Should we leave items out of plurals when we have multiple logical items of different logicalness? (This isn't a completely academic problem, since you could in practice have a couple of new keys in a room that you want to pick up, without detaching keys you already have on the keyring. One easy way to solve this would be to use a different command for detaching from the keyring - require 'take key off keyring', and say Illogical('if you want to take the key off the keyring, say so'); but this could be irritating when you just want to take the key off the keyring.

follow: should replace command with actual command used for last observed travel; alternatively, reproduce the preconditions and verification for the travel via the connector
Player character vocabulary - either add switchPlayer to fiddle with the dictionary, or have some other way to route 'me' and 'my' to the current player character (using matchName, for example).
Move travel preconditions into the connectors after all. Just create an openDoor precondition type that is instantiated with a specific door to open, rather than attaching it to a command object.
send bob into dark. follow bob into dark, then:

>follow bob
Bob is right here.

>bob, u
You see no bob here.
>bob, n
>g

We're not checking to see if the actor is still in scope.

>n. open door.
>close door and sit down
--> endless loop - weird problem with token indices for 'sit down' part of grammar tree. The token indices for the second predicate are for the entire command, strangely, so we think we need to parse the entire phrase again and again.

This has something to do with the empty dobj noun phrase. Same thing happens with 'close door and open' (which also has an empty dobj), but not with 'close door and look' (which takes no dobj) or 'close door and sit down on floor' (which has a non-empty dobj).

when changing sensory contexts, don't flush output; instead, consider sensory context when queueing reports, so that we simply don't queue a report unless it's visible in the sense context
when a door closes from the other side, show a message about it
Status line messages for nested rooms, sitting, lying
Standing needs to be able to specify what you're standing on in some cases, such as platforms. Is it only the floor that won't?
when standing, make sure we move actor to room
when sitting/standing, make sure we do all the necessary work of travelTo
Chairs, beds (make Room not Fixed? or make a deeper base class for BaseRoom, with Room: Fixed, Room?)
sit/lie on floor: actor doesn't actually need to stand first, if the actor is already sitting/lying in the room. In other words, if we're just changing from sitting to lying or vice versa, and not changing containers, we don't need an intermediate standing state. We need something like standing-or-in-obj instead.
sit/lie/stand on - when checking that we can enter a nested room, make sure we're in the immediate container of the nested room
sit/lie: need to configure prep for 'on' vs 'in' (since we 'sit on' some things but 'sit in' other things, and likewise for 'lie on' vs 'lie in')
sit on floor, then sit on chair -> no intermediate 'stand'. This might not be important, since it won't involve actual movement, but it's a little weird, and it would be nice to make it consistent with the real nested rooms.
'sit on floor' doesn't work if run from a doubly-nested room (or deeper)
sit on armchair, then stand on dais: "already standing on the dais". Need to deal with an implied command that effects the change required of the main command.
Might want a precondition for travel more general than 'standing', to allow for standing in vehicles and on platforms
list actors in a nested room when describing the room
maximum seating capacity for a chair
in box: try 'e': "you can't do that from the main platform".
in box: in the dark, should probably have the enclosing room in scope
in box: when closed and we have light, definitely should have the enclosing room in scope
in box: should not be able to try to get out of box when we can't sense beyond the box - it should serve as top-level location for the purposes of the implied actions in this case
When in box, and box is closed, list contents of box as top-level contents of room.
Listing room contents should show contents of fixed items to any depth. The first-level contents of anything not listed should by default be listed.
Vehicles
traveling - don't use actorStanding, but use travelerReady instead. If the traveler is an actor this turns into actorStanding (or whatever is appropriate for the actor); for a vehicle it's probably nothing, but could be used to check for things like the doors being closed on a car or seatbelts being fastened.
Vehicles: when PC is riding, we get no description of the new room
Vehicles: put tricycle on platform; get on tricycle; ride tricycle: we don't seem to move the tricycle off the platform first.
Vehicles: departing should use definite article
Vehicles: departing doesn't list riders (presumably they're out of scope by the time the departure message is generated; need to save sense info or something)
sample game: implement 'ride tricycle'
sample game: when getting on tricycle, mention how to ride it
Put x on floor: if we're in a nested room with a drop location that isn't the main room, we'll perform a "drop" in the nested room. Should actually move to outermost room if possible. (Maybe we need to leave the nested room as a precondition.)
Vehicles: need a convenient way to put up vehicle barriers, so that you can walk through a door but not ride through, and vice versa
'follow me' mode for an actor
recognize (in input) reflexive pronouns referring to target actor (bob, examine yourself)
Allow things to be placed out of reach from within nested rooms (canReachFromRoom).
change from LangThing to 'modify Thing', etc.
change "bob, tell me about x" into "me, ask bob about x"
'push/move dobj to iobj' - for things like building staircases ('push crate to window'). Synonyms might include 'push/move/put dobj under iobj'.
Fix follow problem: "bill cannot stand in closed box" is reported even though we're out of range of the sense.
Allow some types of nested rooms (platforms, booths) to be their own follow locations, so we can follow an actor into such a room
Might want to keep pronoun antecedents per actor. When an issuer gives a command to a target, the target should copy all of the antecedents from the issuer.
for 'in' and 'out', by default ask for a direct object if there's no evident travel connection for the direction
remove all preconditions from noTravel, as we know travel will not occur and hence don't need to meet any conditions
'push obj north' (and other directions)
for "follow", track actors in vehicles as well as the vehicles themselves
'draggable' class, implementing a general framework for pushing objects from one room to another
add a push-travel barrier that works like the vehicle barrier, but for traveling while pushing objects
Change the barrier mechanism so that barriers are listed on the connector, rather than being part of the connector. This is needed because the connector is in many cases directly addressable - putting the barrier in front of the connector as a proxy connector doesn't work when you can reach the connector directly, making an end run around the barrier.
Change the listing mechanism so that showList is a method of the ShowListInterface class, to simplify argument lists.
Handle nested grouping. Use this for equivalents in groups.
For group listings, don't add a group when there's only one group due to equivalence. We currently say this, which is awkward: "the jar contains two coins (two copper coins)."
Equivalent listings - eliminate as a separate special case and simply list equivalents using a listing group. Add an Equivalent Group which implements a simple listing group for equivalents; by default, the group simply shows the count.
For our silver/gold/copper coins in the sample game, make sure the grouping mechanism can list like this: "eight coins (three silver and five gold)". Note that the names in the parens leave off "coins" and just list the number and adjective.
Move all of the lister objects into the messages module, and eliminate the secondary indirection through the libMessages object. Simply make the lister itself part of the messages module, and put the messages directly in the lister.
Get rid of the unnecessary list flags LIST_WORN and LIST_INVENTORY, replacing them with parameterization at the listing method level instead.
Use the generic list mechanism to list actors in a room. By default, each actor simply lists separately, but it should be possible to group actors using the normal listing group mechanism ("Bob and Bill are at their desks", for example).
Can we make the disambig list use the normal listing mechanism?
Add a "specialDesc", similar to initDesc but for more general purposes. specialDesc should turn into initDesc when initDesc is applicable.
add "the former" and "the latter" as disambiguation responses
For nested rooms, break out the "staging location" test from checkActorReadyToEnterNestedRoom into a separate routine. Use a list of possible staging areas given by a separate property for easier overriding.
For nested rooms, use a separate "exit destination" property for the destination of a GetOutOf action, rather than assuming that we always move to the enclosing location.
Add a "high nested room" that can't be entered except from particular staging areas due to height. These is a fairly trivial specialization: first, don't solve the problem using implicit commands unless the actor has already solved it before; second, the default failure message is something like "you can't enter that from here; it's too high up."
Rename the '*predicate*' production classes to '*command*'
Base all command (formerly predicate) classes on a common CommandProd base class
Add a resolveAction method to the command classes. This method retrieves the action from the command. In English, this simply retrieves the 'predicate' match tree object, which is always based on action; other languages, especially those that use case markers rather than word order to encode phrase-role information, can customize this behavior appropriately for their grammatical systems.
When scanning command parse matches, we can immediately eliminate any match for which resolveAction fails to find a valid action.
Use separate listers for room/object contents lists and inventory lists. Use separate show-list-item methods for inventory items.
For keyrings, show contents of keyring in room/object/inventory contents as a sublist: a keyring (attached to which are an iron key and a bronze key)...
Allow queuing pending actions for an actor, not just token lists. Make it possible to insert a pending action at the beginning of the queue (for continuation actions).
Use a separate listing flag (isListedInInventory) for inventory list inclusion. For actors, make this true by default, even though actors are not listed in room descriptions.
Dispenser and Dispensable, for matchbooks and the like
Collective objects for cases like "book of matches" and "matches" - when we type "take matches," we should interpret this as the book of matches (i.e., "matches" == singular) rather than as the individual matches ("matches" == plural).
Matches - flammable objects that are self-igniting
Matchbooks
Include a checksum in saved state files, and check at start of load. This will help avoid attempting to restore a file that was corrupted.
Add a tentative pre-resolution pass, for the later-resolved object of a two-object action, that runs before the resolution of the earlier-resolved object. Don't perform full resolution on this pass; specifically, don't count any problems against the ranking results, and don't ask for help interactively.

Make use of this information in 'verify' routines for the earlier-resolved objects when applicable. For example, for "take from ", we resolve the indirect object first, so we don't know the final dobj resolution when resolving the iobj; so, use the tentative information so that we can rule an iobj illogical if there's no tentative dobj inside the proposed iobj.

Might want to clean up the other-object business in askMissingObject and related routines. This scheme doesn't feel properly generalized. For that matter, we might want to rethink the whole scheme so that it's subclassed by and handled in the action classes, rather than parameterized, since the parameterization scheme seems hokey and brittle.
Remove the otherObjectList and otherObjectWhich stuff when we've cleaned up the corresponding code. This information should now be available via the tentative resolution lists instead.
When lighting a match as part of an implied command, reduce the burn time by one turn - this ensures that lighting the match as part of another action won't artificially extend the match's burn time.
candles, torches, oil lanterns - flammable objects that must be lit with another item, such as a match
defaults: when there are two or more equivalent items that could be used as defaults, pick one arbitrarily
for implied transfers into a bag of holding, put the indirect object of "take from" last in the affinity list
for implied transfers into a bag of holding, other things being equal, transfer the least recently acquired object first
food items
Change the logic of checkActorInStagingLocation so that it doesn't assume that we can reach a staging location just because we're indirectly in the location. Instead, add a "choose staging location" method so that rooms can override the staging location chooser with appropriate special-case code when needed.
Add issuing actor and target actor parameters to resolveAction(), so that the resolver can tell what reflexive pronoun phrases mean. (In many languages, reflexive constructions are structurally part of some predicates, and affect the meaning of the predicate, so the identity of the speaker and of the subject must sometimes be known to correctly determine the interpretation of the predicate structure.)
sample game: reject just plain "match" in the matchbook's matchName
touch scope: include contents of matchbooks and keyrings, so that these can be used even in the dark
Add an associated odor and sound property to each Thing. This points to an object that encapsulates the object's sensory emanations. Always add these objects to scope when the associated objects are in scope, with the same properties.

Make "listen to x" and "smell x" defer to the associated objects.

For scoping, a sound or smell doesn't have to place the main object in scope, but rather can just put the intangible sensory emanation in scope. For example, if an alarm clock is buzzing, the 'buzzing sound' object would be in scope but not the alarm clock itself. (On the other hand, some sounds, like a ringing phone, might be sufficiently distinctive as to place the main object in scope. But for those cases we can simply associate the sound/smell with the main object.)

We need different odor/sound descriptions for different situations:

>listen to phone
It's ringing.
==> phone.soundDesc

>listen to ring [phone visible]
It's coming from the phone.
==> sound.soundWithSource

>listen to ring [phone not visible]
It sounds like a phone.
==> sound.soundWithoutSource

>look [phone visible]
...
The phone is ringing.
==> sound.soundHereWithSource

>look [phone not visible]
...
You can hear what sounds like a phone ringing.
==> sound.soundHereWithoutSource
Don't open a footnote when the PC isn't seeing the text with the footnote reference.
Add sense emanations to inventory displays.
For sound/smell objects, add options to control ongoing announcements of sensory emanations:
  • always show on room description, or only show on explicit "look" descriptions (and corresponding sense commands - "smell", "listen")
  • show every n clock ticks
  • show every n clock ticks for m iterations, then go to a secondary message every p clock ticks (for something like "the phone is still ringing").
Room descriptions should be differentiated according to whether we're explicitly looking or merely entering a new room.
When a noise's or odor's source is not visible, and the object is examined (via "examine", or via "listen to" or "smell" or whatever), it might be desirable in many cases to show the apparent source, which is to say the visually opaque obstructor, and describe the sound/odor as coming from inside/outside/behind/whatever the obstructor.

The obstructor can be easily found with gActor.findOpaqueObstructor(sight, obj) (where 'obj' is the noise or odor object). Once the obstructor is found, we'll have to generate an appropriate message, which will require a new method parallel to obs.cannotReachObject(obj) - perhaps we could call it obs.describeSoundObstructor(obj) etc - which would display "The ringing seems to be coming from inside the box" and the like.

This entire mechanism would not be used when the obstructor itself cannot be seen, such as in the dark.

Consider:

>look
...
You can hear what sounds like a phone ringing.

>listen to ring
It sounds like a phone.

>x phone
You don't see any phone.

It would be better if that last line were:

>x phone
You can hear a phone ringing, but you can't see it.

To deal with this, adjust the touchObj and objVisible preconditions so that it provides better feedback for an object that can be heard but not seen.

Write a common main() that most games will use, with appropriate hooks for showing the introductory text and so on. Or, perhaps better, write a standard initialization routine that the game's main() can call.
Provide an author-configurable option to process multiple orders to NPC's synchronously - so, if you say "bob, go north, get all, go south", the PC doesn't get a command line prompt again until Bob finishes the whole set of commands.
It might be worth considering making the disambiguation responses for &noMatchDisambig and &disambigOrdinalOutOfRange more interactive. Currently, if you give an invalid response to a disambig question, you get an error and no chance to retry. Something like this might be more intuitive:

>take ball
Which ball, the red ball, or the green ball?

>third
There weren't that many choices - did you mean the red ball, or the
green ball?

>orange
That wasn't one of the choices - did you mean the red ball, or the
green ball?

However, if the response looks like a valid response but doesn't give us a match, and it also looks syntactically like a valid new command, treat it as a new command.

Generic script objects: an object for which we call a method automatically each turn it's active. We'd keep a counter that we'd automatically advance on each call. We should probably use this generic object to implement text lists.
Generic text lists: an object that encapsulates a list of messages to display, one per turn.

Text lists should be linkable to a common counter object, so that the lists are synchronized on the same counter. This lets you have separate lists for each room, but keep the timeline in each list the same.

It should be possible to associate a text list object with a room instead of coding the room's text list in-line in the room.

Room text lists: a list of atmosphere messages we cycle through while the player is in the room. This should be totally automatic, so you just program the list and the library automatically sets up a daemon to run through the messages.
When describing an object, show the special descriptions for any contents of the object, just as we would list them in room descriptions. This lets us see the special descriptions for contents, without having to write any additional descriptive code in the container.
consider:

>take coin
Which coin, silver or gold?

We should probably keep the copper one in the list. In particular, we probably shouldn't remove an item from consideration for disambiguation just because it's less likely - once we've determined that the noun phrase is ambiguous, we should offer all logical matches, even when they're less likely.

consider:

>take coinc
Which coin...?

>take tricyc
The word 'tricyc' is not necessary in this story.

>take tricyc
Taken.

For some reason, truncated words are treated as misspellings when they appear in disambiguation responses.

Can we cache sense information to speed up resolution a bit? In particular, we should be able to cache sense information during the noun phrase resolution and verification steps, since these do not normally involve any game state changes; once execution begins, though, caching should be turned off. (This is probably the optimal set of trade-offs: on the one hand, the game and library should never have to worry about cache invalidation - the library only needs to turn caching on before starting the parsing phase and then turn caching off before beginning the execution phase of a command; on the other hand, we should derive substantial efficiency from caching during the parsing phase, because this phase in particular tends to evaluate a lot of sense information.

(Experimental code has been added, conditionally compiled based on the SENSE_CACHE macro. We'll leave it on for the time being to get some experience with it to see how well it works.)

Preparsing
Just-out-of-reach containers. Create a container type that puts its contents in the distance for 'touch' purposes. This can be used for things like items on the ceiling. We'll need a way to make this vary depending on the source object, so that, for example, something could be out of reach for an actor standing in the room but in reach if the actor stands on a desk.
When using 'again' with a command that had an interactive disambiguation response, the response 'that wasn't one of the choices' is not appropriate since the player doesn't get to respond.
cache touch paths (and canTouch information) the same way we cache sense paths
For OutOfReach containers, we shouldn't be able to touch the container itself, since not only the contents but the container itself is meant to be out of reach. To do this, add a PathTo traversal for the last item in a path (and while we're at it, add the symmetrical PathFrom for the first item in the path), and in OutOfReach's checkTouchViaPath, treat PathTo the same as PathIn.
'down' from a platform should be 'get off platform' by default
add 'debug' verb (to break into the debugger)
Real-time events: on save/restore, adjust system clock basis to keep everything in sync
Real-time events: design a class for the events
Real-time events: integrate with the scheduler and command reader
Real-time events: when an interruption occurs, catch any output and automatically cancel the interrupted input
Fix problem with quoted strings in literal phrases (as in 'type "hello" on typewriter')
Fix problem with 'throw': it's illogical to throw something at an object within the object being thrown
Change all of the exclusion list phrases to terminalNounPhrase rules - otherwise, when they exclude everything, they get misinterpreted as ((all but x and y) and z) which turns into (z and z). Everything with a 'but' should turn into a terminalNounPhrase, just like the plain 'all but <list>' rule.
Make 'me' symmetric with 'yourself' in the grammar. In particular, 'me' should refer to the issuing actor, which is not necessarily the player character.
fix problem with "yourself, jump"
For chairs and the like, we might want to consider an object owned by an actor if the actor is occupying the object (so, "bob's chair" is the chair bob is sitting on).
When taking an item, reduce the likelihood that an item being carried by another actor is the one being taken.
Provide a way of distinguishing lit and unlit equivalents in a list. Likewise for worn and unworn objects. Should probably add another list group object after the equivalent grouper, and in this object we should provide one group for lit objects and another for unlit. The desired output is something like this:

  You are carrying three matches (one lit), two flashlights (one
  providing light).
Move the inheritNext into the behavior as the default native 'inherit' instruction. Remove inheritNext from library source files.
the dagger shouldn't show up in the 'x desk' description when the dagger's initial message is still being shown
Add macros to make grammar rules for predicates easier to read.
Improve the module names (adv3g -> parser.t, adv3v -> verbs.t, etc)
provide .t3m, .tdc for sample game with proper directory set-up
accept 'all ' and the like
build the #include file list in Workbench automatically when creating a project, and when explicitly asked via a new command "scan source files for #include"
compiler: warn on finding a backslash outside quoted text in front of anything but a newline
Add a simpler, more structured way to add a single paragraph break. Use a new pseudo-tag, "<.p>", to indicate a paragraph break; process this tag just before writing output to the console.
All library input functions should coordinate with command reports to turn off output capturing.
Add a finishGame() function offering restart/restore/undo/quit options
Build a proper set of Workbench sample games
Freeze the real-time clock while we're waiting for inputFile results, while we're saving/restoring a file, and while we're waiting for an inputLine when real-time events are not allowed. All of these should be considered to be outside the normal real-time flow of the game, so none of them should consume any game real-time.
In showList: mix groups and singles in the original order of lst, rather than displaying groups and then singles separately. This is important because we're not preserving the original ordering when the list is sorted and it has subgroups. We can't guarantee that we'll preserve the sorting order, of course, since grouping could put together items that weren't consecutive in the sorting order, so let grouping prevail when there's a conflict; but when possible, keep the order the same. To do this, we should have a single loop that traverses the original lst; for each item, if it's a single, show the single, otherwise show its group. Only show the group for the first item in the group; once we match a group, mark the group as used so we don't show it again.
'+' should consistently concatenate individual items from a collection on the right-hand side, whether the right-hand side is a list, vector, or array
'+' and '-' should work with an Array as the left-hand side
Delete class Array. We'll have to change the function object stuff to use Vector instead of Array.
Make Vector+val and Vector-val return new objects - do not modify the original vector in place
add Vector.appendAll() - appends list elements in-place; maybe Vector.removeValue() and Vector.removeAllValues() to do '-' in-place
LookupTable.forEach: remove the 'key' argument to make its interface the same as everything else's.
Everything with a forEach: add a forEachAssoc(key, val) method alongside forEach.
for Openable, if the object isn't transparent looking in, implicitly open the object for "look in"
Iterators: add getCurVal and getCurKey methods.
Fix bug: 'listen' or 'smell' shows no response when there are things that nominally have Noise/Odor associations, but none of the Noise/Odor objects actually has anything to say at the moment.
Dictionary: add a way to iterate over the entries in the dictionary.
For inputManager.inputLineBegin and inputLineEnd, use a new style tag "<.inputline>" rather than coding "" directly. This makes it easier for a game to customize the input font setting.
Add access to the "restore code" (the second argument passed to restoreGame()) for PostRestoreObject instances by adding the value as a class property of PostRestoreObject.
Change the Thing template that ends with "desc" @location to end instead with @location "desc" (i.e., reverse the desc and location property order). Since "desc" can be lengthy, putting the location first will make the template more readable.
Add before/after command separation messages in libMessages, to allow for finer-grained control over the formatting of command responses.
Check dark travel before checking any barriers - move the call to gActor.checkDarkTravel into the TravelConnector methods that call gActor.travelTo, since this must be called as a separate phase before the travel connectors do any of their other work.
Make Illogical() the most illogical ranking, and make IllogicalNow() slightly less illogical (i.e., invert the old ordering of Illogical and IllogicalNow).
Add Action.setMessageParam(), to allow message processors to set their own special message parameters that they can use in expansion text. This would be better than constructing messages partially with the "{it obj/him}" mechanism and brute-force string concatenation, because it would allow the concatenated bits to participate in the same processing steps, such as reflexive pronoun conversions.

Add a macro (gMessageParams) to simplify the syntax for adding new message parameters.

"x me" doesn't work in dark (target actor of a command should always be in scope)
Remove 'actorResolved' check from execCommandTokens - this check is vestigial (it stopped being necessary when we started pulling out the actor phrase after resolution and re-parsing the rest of the command), and makes it impossible to have multiple levels of indirection, as in "tell bob to tell bill to go north".
Refactor FirstCommandProdWithActor (parser.t) to move the actor resolution stuff into a separate CommandProdWithActor that FCPWA inherits from.
>bob, get ball
Which ball?

>z
Bob waits...

The "z" should have a default target of the player character, not Bob.

Allow things like "tell bill to tell bob to go north". In particular, fix execCommandTokens so that it doesn't assume that only one actor can be resolved per command.
Keep first-on-line information in the PendingCommandInfo. (This requires changing the interfaces to all of the Actor routines that create pending command info objects: addPendingCommand, addFirstPendingCommand, addPendingAction, addFirstPendingAction.)
Add a noun-list grammar rule for singleNoun, so that if we match a noun list in a place where a single noun is grammatically required, we can generate an appropriate message rather than failing to match grammar.

Symptoms fixed: "ask bob and bill about box" -> "you see no bob and bill here"; "give coins to bob and bill" -> "you see no coins to here".

Add a point-of-view argument to BasicLocation.lookAround, so that a room can be described from a point of view other than the actor doing the looking.
Fix: paragraph break in response to getting out of white box while the box is closed (in the platform room). (The last report - "Okay..." - is displayed on the same line as the beeper noise.)
Can we make every kind of noun phrase production derive from a common NounPhraseProd base class?
Allow multiple actors per command line, tads2-style, as in "bob, go north. look. bill, go south." (In tads 2, a new actor can be specified after a period.) However, only allow this in "synchronous" mode, where the issuing actor waits for all commands to NPC's to complete before taking another turn; in asynchronous mode, do not allow mid-command target actor changes, because it's too confusing.
Fix problem in touchObj precondition: we assume that we can find a valid touch path, so we traverse the path without checking to see if we got a nil path. In some cases (such as when the target object is in a location not sharing any common container with the actor), there is no touch path at all, so we need to deal properly with this possibility.
Fix visual command separation for 'undo' after a multi-command line, and for a multi-undo command line.
Modify/replace/delete grammar rules. Add new syntax:

  modify grammar production(tag): : 
     
  ;
  replace grammar production(tag): : 
    
  ;

To make this workable, the "production(tag)" names of grammar match objects must be unique; change the compiler to enforce uniqueness of these names.

Also, change VerbRule to include a tag: VerbRule(tag). This will allow modify/replace to be used with VerbRule(tag) definitions to replace library verb grammar rules.

Add a mechanism for remapping a two-noun-phrase action to a different action after resolving first object. For example, allow mapping ATTACK x WITH THROWING STAR to THROW THROWING STAR AT x.

To do this, add a new pair of properties to TIAction: remapDobjProp and remapIobjProp; define these properties using the same template style as the rest of the properties in DefineTIAction. Add a new remapTIAction() macro that can be used from within a remap() method to remap a command.

Note that only the first-resolved object can be remapped, because the whole point is to effect the remapping before resolving the second-resolved object, so that the second-resolved object can be resolved using the rules of the new action rather than of the original action.

In BasicLocation.lookAround, remove the point-of-view object from the list of objects to be described, rather than removing the actor.
Move the grammar convenience macros (VerbRule, SingleDobj, etc) out of adv3.h and into us_eng.h - these are all specific to the English verb grammar, so they should be in the English-specific definitions rather than the language-independent definitions.
If a Daemon is created with an interval of zero or less, it'll put the scheduler into an infinite loop invoking the daemon, because the daemon will be permanently schedulable. Force the interval to at least 1 on creation.
Add a lookAround() method to Thing, to generate a description from the point of view of the Thing. This is necessary to allow things like remote cameras. This method should call the container's lookAroundPov() method (which must also be added to Thing) with the point of view set self; this algorithm will eventually reach the innermost room-like object, which will display the appropriate room-like description.
Add a PAUSE command, to stop the real-time clock for games that use real-time events.
Add a grammar notation that specifies a match for one of several vocabulary properties. With the '<prod>' grammar that was being kicked around for a while, the notation '' was appealing, but without the angle brackets, it's not obvious what the equivalent syntax would be.

Use angle-bracket notation, even though we don't use it for individual tokens: <nounM nounN nounF>

Add an explicit 'grammar' declaration with no matchable rules, so that production names can be declared without actually creating rules for them.
make 'inherited' after 'delegated' more consistent with the regular inheritance behavior (in particular, the relatively new dynamic inheritance search algorithm will fail to find a class related to 'self' in the delegatee's superclass tree, so there will be nothing to inherit; ideally, we'd have an 'inheritance target' in the stack frame in addition to 'self')
NestedRoom.getTravelConnector refers to gActor. The reason the method looks at gActor is that it wants to check if the actor can see the containing room, so that it can get the connector from the containing room. Add an 'actor' parameter to getTravelConnector(), so that we can specify an actor explicitly and thus ask for travel connectors outside of turn execution contexts.
SouthwestAction is missing from action.t
noTravelIn, noTravelOut, noTravelDown - these need to override isConnectorApparent to return nil.

Add redirectTravelIn, etc, which do show isConnectorApparent = true.

Add newActionObj alongside _newAction: the new function takes a pre-created action instance, rather than creating a new instance itself.
Add a getBestMatch() method to ResolvedTopic to retrieve the top-ranking object. By default, provide an implementation that simply returns an arbitrary object from the strongest match list. Games and library extensions could customize this to use a different topic picker mechanism as desired, but providing a decent default implementation would help authors get started without having to worry about coding their own topic framework if they didn't want anything specific here.
Add a PreRestartObject, analogous to PreSaveObject, to allow any special actions to be performed before a restart.
execCommandTokens should probably switch the sense context upon discovering a target actor different from the issuing actor, before actually starting to execute the command.
Move all of the output management into discrete OutputStream objects. Associate filters, notifiers, capturers with individual streams. By default, create one stream for the main text and another for the status line.

NOTE: This change affects all of the notifier, filter, monitor, and capturer function interfaces. All of these formerly global functions are now methods of the OutputStream class.

Regarding touch path calculation (see e.g. touchObj precondition): should we be finding a touch path to an object in a separate location not connected by containment, but connected by a SenseConnector?
Fix basicScoreChange reporting for changes of 1 (or -1) points: use singular "point".
change the T3 Workbench .tdc format to use .t3m files
vmrun: respond to Ctrl+Break to break into debugger
"parse-debug" command: accept the command without the "on" or "off" specifier, and simply invert the current mode.
Add a nounMultiList production alongside nounList. Use this production in matching singleNoun, rather than using the regular nounList, so that we don't create spurious additional matches for the degenerate single noun phrase form of nounList.
For bags of holding, check (in Container.tryPuttingObjInBag) to make sure that the object being moved into the bag actually fits, and don't even try if it won't. This will avoid spurious attempts and failures to move things in to a bag of holding implicitly after the bag becomes full.
Refactor the CommandProd-based productions in parser.t to move the methods into new classes, and base the defined grammars on the classes. This will facilitate adding new commandPhrase grammar rules by allowing the existing behavior to be re-used via inheritance.
Add a new Actor property (revertTargetActorAtEndOfSentence) that, when set, tells the parser to consider target actor designations ("bob, go north...") in effect only until the end of a sentence. When the property is set to true, switch the target actor back to the original issuing actor at the end of each sentence.
As a convenience, add a new StyleTag subclass, HtmlStyleTag, for tags with different renderings in HTML and plain text modes.
Add some new style tags:
  • <.a> - for <a> text (and use it for all links we generate)
  • <.statusroom> - for the room name portion of the status line
  • <.statusscore> - for the score/turn count portion of the status line
Add inputManager methods getKey() and getEvent() that provide covers for inputKey() and inputEvent(), respectively, the integrate with the real-time manager and the reports gatherer in the same manner as inputLine.
Don't use [badness] to reduce the priority of ordinal lists in disambiguation responses; instead, use the ranking mechanism to give ordinals a lower ranking than noun/adjective interpretations.
Fix looping problem with answering 'g' to disambiguation queries. (Don't store the response to a disambiguation query until after we actually know that the input is an answer to the query, as opposed to a new command.)
Do not notify sense path in moveInto() when traveling. (Travel uses the separate TravelConnector mechanism, which doesn't necessarily map directly onto sense connections; we don't want to attempt to notify sense connections of travel because we don't actually use them for travel.)
When disambiguating interactively, if the response isn't in the full list, check to see if it's in the full scope list. This allows for cases where the player really wants to try a command on something that we decide isn't logical - it's better to let the player try than to deny that the object is among the possible choices.

We should test the scope list as a second pass rather than allowing the full scope on the first pass. So, we should try first as we do now, limiting scope to the narrowed ("logical") list; then, if that fails, we should try again with the full scope list.

Add an equivalent of the tads 2 "->" syntax: this syntax allowed routing messages for one object to another object. For example, if we wanted to route "open desk" to "open drawer", we would put "doOpen -> drawer" in the desk. Define a new macro:

  dobjForwardTo(Open, drawer)

This is used in place of a dobjFor().

(This was from Phil Lewis, who suggested "reroute" as the name and also suggested a slightly more general format: dobjReroute(drawer, Open). It might also be useful to be able to specify the verb to forward separately, so that you could handle a verb with both a different verb and different target object, but the action is probably the same in most cases, so it seems more convenient to be able to omit it.)

In roomSmellLister (and roomListenLister), add a custom isListed method that checks a new property of the target object, isSmellListedInRoom (isSoundListedInRoom in the case of Noise objects). Set these properties to true by default. Add an isListed override to smellActionLister (and listenActionLister) that simply returns true. This change allows an Odor/Noise to list in response to a >SMELL or >LISTEN without also showing in the normal room description, as it would by default.
Propagate the results of an action up to callers by returning the CommandReportList. Actually, we'll have to return a list of CommandReportList objects, since one CommandReportList is generated per iteration for an iterated action. The aggregate list could be stored in a CommandResults object, which could provide high-level analysis methods (e.g., isFailure()) to interpret the action results.

To do this, return a CommandResults object describing the command reports list from newAction(), nestedAction(), and so on.

Provide a way of specifying a subclass of CommandReportList to use when executing a new or nested command. To do this, callers use a new function that takes a particular CommandReportList subclass to use while calling a callback:

  result = withCommandReportsClass(
      MyCommandReportList, {: nestedActorAction(bob, SitOn, chair) });
In the tokenizer, if a word with a "'s" suffix explicitly appears in the dictionary with the "'s", do NOT make the "'s" into a separate token - keep the whole string with the "'s" as a single token. This allows for cases where the grammar has a literal token with a "'s" suffix, and where vocabulary words (such as adjectives) are explicitly defined with "'s" suffixes.
In TravelConnector, allow for a single barrier rather than a list of barriers. If the travelBarrier property is not a Collection of some kind, treat it as a single TravelBarrier.
Add a OneWayRoomConnector to make it easier to define a connector that connects one room to another but not vice versa.f
Add checkTravelConditions() to TravelConnector. Call this from checkTravelBarriers(). By default, this does nothing; instances can override it to apply special conditions to the travel in lieu of creating a separate TravelBarrier object when the conditions don't need to be re-used in other connectors.
Fix indentation problem for grouped items in INVENTORY TALL lists.
Hide intangibles from 'all' by default. Noises and odors should probably be included in 'all' for LISTEN/SMELL.
When trying to put objects into a bag of holding to make room to take a new object, don't even try moving an object that contains the bag of holding (because doing so will just fail with a message "the is already in that").
Need a better way to say "the one in the room" than showing the room name, because that's not useful in input. Should say something like "the match on the floor".
Once everything in a disambiguation list is a basic equivalent, we could use the 'name' of the objects to display the prompt, rather than using the input text.
In Thing.initializeEquivalent, deal with the possibility that we have a base class with a separate equivalent grouper. Create a separate grouper for the subclass by testing to see if equivalentGrouper is defined directly by the class object. If there is an inherited grouper, we must create our own separate grouper for the subclass, AND we must take the superclass grouper out of the listWith for the subclass (since it will inherit the base class listWith by default).
In action.t around line 1532, when we're adding the UnclearDisambig flag, we might want to suppress the flag if the object we selected and the objects we rejected are basic equivalents of one another. This avoids weird situations such as

  >light match
  (the match)

when we have one match we're holding and another in the matchbook: we choose the one we're holding over the one in the matchbook because of the must-be-holding precondition to lighting a match, but mentioning which one we're choosing is weird because it doesn't tell us anything.

>drop match
Which match, one of your matches, or the match in the matchbook?

>my match
Which match, one of your matches, or the match in the matchbook?

...etc. The problem is that we're taking "my match" to mean any match in my possession for parsing purposes, whereas we differentiate holding from indirectly owned. Ideally, we'd prefer to treat "my match" as "the match I'm holding directly" when it's ambiguous.

Add a disambigName property, and use it instead of the ordinary name when generating disambiguation prompts. (We'll need aDisambigName, theDisambigName, and countDisambigName as well to round out the set.)
Add "x in y" grammar.
Add a mechanism for distinguishing equivalents with different states:

  Which candle to you mean, the lit candle or the unlit candle?

  -but not-
  Which wax do you mean, the unlit candle or the seal?
  -which should just be
  Which wax do you mean, the candle or the seal?

Use this mechanism to create a base class for light sources. We can use this for things like matches and candles. The disambig name for an object is "lit x" or "unlit x", so we add the adjective "lit" or "unlit" according to state. Check the adjective in parseName.

Use this same mechanism to specify by owner when owner is a distinguishing factor - "which gas mask do you mean, yours, or teeterwaller's?"

To do this, associate with each object a list of "distinguisher" objects. A candle might include the "lit" distinguisher object, and probably all objects would include the "owner" distinguisher. We'd make a list of all of the distinguishables in common to the set of equivalents, then run through the common set. For each one, we'd ask the distinguisher if the list of equivalents we have is distinguishable via this distinguisher. The "lit" distinguisher would return true if the objects were all in different "lit" states, and the "owner" distinguisher would return true if all had different owners. We'd stop at the first distinguisher that can tell all of the objects apart. The distinguisher would give us the method to call in each of the objects to list its distinguishing name - the "lit" distinguisher would call the litName property, for example, which would display "the lit candle" or "the unlit candle"; the "owner" distinguisher would display "yours" or "teeterwaller's".

The response would have to be handled by the normal disambiguation response mechanism, so the objects would have to conspire with the distinguisher to have the proper adjectives. This is easy, though, because the objects are the ones displaying the adjectives in the first place when they show the disambiguation list.

If we find no distinguisher that can tell all of the items apart, we'd look for one that can tell at least some of the items apart, and phrase it like this: "the lit candle, or one of the unlit candles". If they select one of the ones indistinguishable with this distinguisher, we'd iterate, and maybe pick it up with a separate distinguisher next time.

If none of the distinguishers can tell any of the objects apart, we'd simply choose one arbitrarily like we do now.

>take candle
Which candle do you mean, the lit candle, or one of the unlit candles?

>unlit
Which do you mean, your candle, or one of Bob's?

>bob's
(arbitrarily picking one of bob's several unlit candles)
Taken.

Note that for ownership distinctions, need to offer a way to refer to unowned objects:

>get candle
Which candle do you mean, Bob's, or the other one?
-or- Which candle do you mean, Bob's, or another one?

[responses would include...]
>another / another one / another gold coin / one of the others 
 / one of the other gold coins / other / the other / the other one
 / the other gold coin / other gold coin
Hide actors from 'all' for all verbs by default.
For "put in" and "put on", use roughly the same strategy as "take" for generating the "all" list: include only objects that are directly in the actor's location, or in fixed objects in the actor's location, as "take" does, but also include objects that the actor is directly holding. Keep the same iobj/in-iobj exclusions.
Break up TravelConnector.checkTravelConditions() into two separate methods (canTravelerPass(traveler) and explainTravelBarrier(traveler)), just like TravelBarrier.
Fix Thing.normalizePath() so that it makes two separate passes over the list. It should apply the PathThrough tranformation first, then the PathPeer transformation as a second pass. (Performing both transformations both on a single pass can cause PathThrough transformations to be missed, because the PathPeer changes the list in such a way that the PathThrough transformation doesn't recognize a needed change. The PathThrough condition could alternatively be modified to recognize PathPeer operations, but it's easier to do things in two passes. Note that doing PathThrough first is safe, because it only adds to the list - it never drops any operations that the PathPeer transformation looks at.)
Add distance differentiation for 'examine' descriptions (and for 'listen') and 'smell' descriptions as well). Specifically, add distantDesc and obscuredDesc, and call them from basicExamine according to the current point of view. Add corresponding methods for 'listen' and 'smell' descriptions.
Add automatic state listing to the equivalent grouper. Use a new Thing method, getState, that returns a ThingState object. Use this object to group equivalents by state and to show the state of each item.
Use the ThingState object to provide the ordinary status description of an object.
Use ThingState to provide automatic matchName() filtering by state. Add a property to ThingState, stateTokens, that lists tokens that can only be used in noun phrases referring to an object in the state.
Integrate Steve's exit lister package into the library.
It would be nice to have a sorting order for the directions, so we list in a more canonical order: north, south, east, west, northeast, northwest, southeast, southwest, up, down, in, out.
When two exits have the same destination, list them together: "south (or out) to the front yard".
Don't show exits to dark rooms in the dark.
In listing exits from a dark room, maybe we should show exits leading to rooms with light, since travel would be allowed to those locations.
Change the way we figure out if travel from a dark room to an adjoining lit room is possible. Add a separate TravelConnector method that specifically determines if the connector itself is visible to a given actor in a given origin location when the origin location is dark. By default, make a connector visible from a dark location when the destination room is lit. This will provide the customary behavior (i.e., dark-to-dark travel is prohibited, but dark-to-light is allowed on the theory that some of the light from the destination leaks through to the origin, making the connection itself self-illuminating and thus visible even in the dark origin room), while more clearly articulating the rule by making the visibility of the connector the explicit factor in determining whether or not the travel is allowed, and also providing a clean way to override the default heuristic for visibility of the connector for particular connections that should use a different rule.
Add an option to show available exits in the status line (using a terse display that just lists the direction names).
Make Surface.PutOn behave like Container.PutIn with respect resolving the indirect object using the tentative direct object list: if everything in the tentative direct object match list is already on the indirect object, flag the indirect object as illogical.
Sense Event Model: Create a new class, SensoryEvent, that can be used to trigger transient sensory events that actively notify interested observers of their occurrence.
Add convenience macros for remapTIAction that define the full remapping for a dobj or iobj to another action, with the same or reversed noun phrase order:

  dobjRemapTI(OpenWith, UnlockWith)
  dobjRemapTIReverse(FillWith, PutIn)
  dobjRemapTIReverse(ThrowAt, AttackWith)
examineSpecialContents should probably differentiate according to POV and sight viewing conditions.
Change CommandReportList to a SimpleOutputCapturer. We don't actually want to process any notifiers or monitors when capturing report results, because we turn them into a report list that can be reordered. We want to wait until we actually display the reports to notify anything of the output.
>i
You are carrying three matches (one lit), and a matchbook (which
contains two matches).

>x match
Which match do you mean, an unlit match, or the lit match?

>an unlit match
(the match)
The match is an ordinary match.

It would be better not to make an arbitrary choice here, unless they explicitly said "any unlit match". We should have another go at asking for detail in this case.

Add TITLEs to 's in system messages.
Exit lister: if we have multiple exits from a location with nil destination names, we are incorrectly grouping them as though they had the same destination name. The result is that we only see one such exit in an EXITS list. Destinations with nil destination names should be treated as unknown destinations.
Sort adv3.tl alphabetically (except for 'modify' order dependencies).
Add Dan Schmidt's hyphen-converting output filter to output.c
Add Dan's Thing.canBeSeenBy, Thing.canBeSeen, etc. (These are convenience methods, especially for canBeSeen and its like, which operate on the player character, saving a little typing by making 'gPlayerChar' implicit.)
Add Dan's Event.delayEvent, removeEvent methods
Add an event manager method that cancels an event based on the obj/prop combination, to save work for cases where this uniquely identifies the event (this way, the author doesn't have to go to the extra trouble of saving the Event object reference if the event can be uniquely identified without it).
Add a "mass" or "collective" noun property, for objects that refer to collections of large numbers of smaller parts, or continuously-measured quantities: "some popcorn", "some water". (Is there anything that this changes besides changing aName to "some "? Should think about it.)
Add a lower-level base class for Switch, OnOffControl, that keeps track of the on/off state and accepts "turn on" and "turn off". Define the additional switch-specific verbs ("switch" and "flip") only in the Switch subclass - this allows for adding on/off behavior without making something look exactly like a light switch.
Add a GO BACK command to return to the most recent location (if the connector in was reversible).
Keep track of the last "interlocutor" for each actor; this is actor last addressed in a targeted command ("bob, go north"), or the last actor in a conversational command - ASK ABOUT, TELL ABOUT, SHOW TO, GIVE TO. For the conversational commands, if the actor is left out (ASK ABOUT GOLD, TELL ABOUT STORM), use the performing actor's most recent interlocutor as the default, if the interlocutor is still within talking range.
Create a subclass of Actor, UntakeableActor, for an actor that can't be taken or moved. Define an additional subclass, Person, to represent human characters, and provide some custom "fixed" messages for it.
Ensure that the SCORE and FOOTNOTE modules are completely modular, so that they can simply be omitted from the build if not required.
Get rid of libMessages.openBracket and closeBracket; instead, add a style tag, <.parser>...<./parser>. Also, add <.notification>...<./notification> for notifications (score changes, FOOTNOTE instructions, etc.).
Change withBrackets() to parserMessage(), for better documentary effect.
Simplify the menagerie of output filters, notifiers, monitors, and capturers, reducing everything to a single type (filter). Enforce stacking of filters.
Get rid of the paragraph suppression immediately following input. Instead, use the command sequencer to display the appropriate kind of separation.
Rename CommandReportList to CommandTranscript, and rename gCommandReports to gTranscript.
Add resolved object arguments to Actor.addPendingAction, Actor.addFirstPendingAction.
Rearrange arguments to Actor methods addPendingCommand, addPendingAction, addFirstPendingCommand, addFirstPendingAction to move the firstInSentence flag first. This will keep everything consistent with the rearrangement necessitated by adding the varargs resolved object list to addPendingAction and addFirstPendingAction.
When a command is applied to several objects, and processing the command for some of the objects involves implied subcommands, set off the results for the objects with implied subcommands visually, by putting an extra paragraph break before and after each result for an object with implied subcommands:

PO Box: Taken.
red ball: Taken.

iron key:
(First putting the red ball in the duffel bag to make room)
Taken.

blue test booklet: Taken.

brass key:
(First putting the red book in the duffel bag to make room)
Taken.

gold coin: Taken.
gold coin: Taken.
Use a new style tag, <.commandsep>, for command separation, eliminating the method call commandSequencer.startNewCommand(). Using a tag instead of a method call will work better with text that doesn't reach the output stream immediately (for example, text that is queued into a command transcript during command results display).
In Thing.construct(), initialize the new object's vocabulary words and add them to the global dictionary.
Add information on the connector back, if known, to the EXITS display:

  Obvious exits lead north, back to the east, and south to the den.
  ...north; east, back to the living room; and south, to the den.
For NPC's, the announcement/result format for implied commands should probably be changed. In particular, we should probably just treat NPC implied actions as full separate commands:

>bob, north
Bob opens the door.

Bob departs through the door.
For NPC's, don't allow interactive defaulting for an implied command.
For NPC's, if an implied command fails, we should fail with an indication that the NPC needs to perform the implied command:

>bob s
Bob must open the door before he can do that.

>bob, open door
Bob must unlock the door before he can do that.
Get rid of gCommandReportClass and withCommandReportsClass. Instead, add a parameter to _newAction() to specify the transcript class to use.
Add a new BasicLocation method, getRoomPartLocation(part), which returns the immediate container of the given room part, as perceived in that location. Top-level rooms would generally just return 'self' if they have the room part, nil if not. Nested rooms would generally pass this up to their containers, because they don't have room parts themselves in most cases. Nested rooms with their own room parts would handle it themselves.
"x floor" - should list objects that are on the floor; likewise for other room parts (walls, ceiling). Use a separate listing test (isListedInRoomPart(part)) to allow objects to opt out of being listed specifically in such cases.

For special descriptions, add a "room part location" property - this is a purely advisory property used only for finding contents of the floor and so on. When we "x floor", show special descriptions only for objects whose roomPartLocation is the floor of interest.

Re-enable the transcript after prompting for interactive resolution (reading an OOPS response, a disambiguation response, etc).
Add a method to Action to swap the roles of the tentative objects, for use with the 'verify' forwarding for TIAction remapping. Add a call to this method to the dobjRemapTIReverse/iobjRemapTIReverse macros before they forward their 'verify' calls.
When an action is remapped, and we need to announce an object (such as a defaulted object), announce the entire action, not just the object. For example:

>fill bucket
(dipping the bucket into the river)

>fill bucket
(pouring the coffee into the bucket)

This is important because the normal announcements are worded with the intention of being tacked onto the end of what the player typed; when the action is remapped, the action we're actually executing is different from what the player typed, so phrase fragments that are intended to be added to what the player typed are no long applicable.