================================ VERSION 3.0 ================================= ================================ VERSION 3.1 ================================= **** COMMITTED CHANGES - the following are changes that we've definitely decided to make, but which we've deferred until after the next general release because it's too late in the current release cycle to make changes that could be potentially destabilizing. 1. In DefineTAction(TravelVia), remove the override for getCurrentObjects. This override was originally put in place because the direct object of a TravelVia is a TravelConnector, which was at one time based on 'object' rather than on Thing. TravelConnector is now a Thing subclass, so the direct object of a TravelVia will always be a simulation object, hence it's fine to return it as an action object. 2. In RestrictedUnderside, RestrictedRearContainer, and RestrictedRearSurface, the iobjFor(PutXxx) check() routine should call "inherited Xxx()" (where Xxx is the unrestricted base class - the *second* base class in the RestrictedXxx superclass list) to enforce the inherited allowPutXxx restriction. However, we can't just add the 'inherited' call: each of the base container check() methods does a simple reportFailure(&cannotPutXxxMsg), whereas the Restricted classes invoke self.cannotPutXxxMsg(dobj) - same method, different argument list. We either need to rename the cannotPutXxxMsg(dobj) version of the method, OR add the dobj parameter to the parameter-less form of the method. In either case, we could break some existing code. I think the renaming approach is probably better, because there really are two failure conditions here. The lowest impact approach is probably to change cannotPutXxxMsg(obj) to something like cannotPutObjXxxMsg(obj) for all of the various RestrictedXxx containers. This will necessitate a little global search and replace for existing code, but the alternative is to change some much lower level code: the no-parameter cannotPutXxxMsg messages are used in Thing, for instance. **** REQUESTED CHANGES - the following are changes that have been suggested and possibly discussed to some extent on the tads3 mailing list, but whose final disposition is yet to be determined. We might or might not ultimately decide to add any of these features. Priorities, feasibility, and full design details are yet to be determined for many of these. --------- From Eric Eve : In the adv3 command execution sequence, the relative ordering of the "check" phase and the various "before" notifications seems wrong as it currently stands. Currently, "before" happens before "check" - this means that the command can fail in the "check" after the "before" notifications have occurred. Conceptually, it seems like "before" notifications should only occur after the action is committed. Now, this isn't truly possible in practice, because any individual "before" handler could still cancel the whole command - the relative ordering of "before" handlers is arbitrary, so a "before" handler could cancel the action after other "before" handlers already ran. Nonetheless, it probably makes sense to consider "check" as a stronger sort of test than "before"; it's relatively rare for "before" handlers to cancel a command, while testing for cancel-worthy conditions is the whole point of "check." Implementing this change is trivial. The only reason for putting this off until 3.1 is that it seems likely to break a small amount of existing code - most code won't be affected at all, but there will probably be a few existing cases that depend subtly on the existing ordering due to interactions among multiple objects and/or handlers. People are more likely to expect and accept this sort of compatibility risk in a major update. --------- From Michel Nizette [9/1/06]: Would there be any value in implementing pending conversations via regular AgendaItems, like we just did for the boredom handling? Again, the advantage would be simply to provide more control over the order of priority of an actor's various pending activities. Also, should this prove useful, we could then think of adding an Actor.scheduleConditionalInitiateConversation method, which would do the same thing as scheduleInitiateConversation, but would additionally specify an extra condition to be checked as part of the AgendaItem's isReady property. The condition could be specified simply as a callback function that returns nil or true. --------- From Michel Nizette [9/1/06]: Currently, the method Actor.noteConversation is invoked each time a TopicEntry is triggered, no matter if the topic is conversational or not (i.e., marked with isConversational = true or nil). The noteConversation method does a number of things like marking the actor as having conversed on the current turn, and resetting the boredom count. But I'm wondering if this is really what we want in the case of a non-conversational topic, whose typical purpose is to describe a refusal to engage in a particular conversation. If the conversation doesn't actually take place, should there really be any effect on the NPC's behavior? It would seem a little odd to see an NPC delaying a pending conversation for several turns just because he was involved in a sequence of conversational commands that all got blocked in the PC's mind for motivational reasons and didn't result in a real dialogue, for example. In contrast, noteConversation also does a few things that unquestionably make sense for non-conversational topics, like updating pronouns and establishing a default interlocutor: these are interface features independent of what happens in the game world. So, my suggestion would be to distinguish between the situation where a conversation actually takes place (in which case it makes sense to call noteConversation) and the situation where the conversation is only attempted, which we could handle via a new noteAttemptedConversation method that would only set pronouns and the default interlocutor. The library could then simply invoke noteAttemptedConversation everywhere it currently invokes noteConversation, and could additionally call noteConversation in the few places where a real conversation takes place, like the triggering of a conversational topic or a call to Actor.initiateConversation. If maintaining backwards compatibility is important, we could have noteAttemptedConversation call noteConversation by default, but we could make this controllable via a switch. --------- From Krister Fundin [9/3/06]: > bob, follow me "Okay, I will follow you." > g "Okay, I will follow you." The library comments suggest that the latter should not happen. The problem is that Actor.nonIdleTurn() cancels following whenever the actor receives an order. Thus the check for already being in following mode is always negative. [mjr: I actually think the current behavior as shown is fairly reasonable. It would be marginally better to have a message like "I'm already following you." But short of repeating the command ten times in a row for comic effect, I'm not sure players would ever be bothered by the present behavior in practice - if they're repeating the command, they're presumably not sure that Bob was already in follow mode, so they're not going to be bothered by another acknowledgment.] I had already removed this cancellation from one of my projects, since it wasn't appropriate there, but that seems to cause a different problem instead: > bob, follow me "Okay, I will follow you." > g Nothing obvious happens. In this case, Bob can already see the player and thus only makes a call to checkMovingActorInto(), which does nothing since he's already where he's supposed to be. [mjr: This part is worth looking into whatever the disposition of the first part, since the nonIdleTurn() comment suggests that it's okay to override and not cancel follow mode, but apparently that's problematic.] --------- From Krister Fundin [9/3/06]: just a thought (feel free to ignore it): > bob, follow me "Okay, I will follow you." Bob stands on the main platform. versus: > bob, follow me Bob stands on the main platform. "Okay, I will follow you." Doesn't the latter sound marginally better? [mjr: I'm not sure about this; Bob acknowledging first actually strikes me at the moment as the better sounding version] I guess in practice there would be a command separator between the two reports, but that could actually improve things when two or more implicit actions are needed: > bob, follow me "Okay, I will follow you." Bob stands on the main platform. Bob stands on the red platform. versus: > bob, follow me Bob stands on the main platform. Bob stands on the red platform. "Okay, I will follow you." --------- From Krister Fundin [9/8/06]: How about adding a command joiner report and corresponding pseudo-tag? Any combination of joiner and separator or separator and joiner would just remove both. I can think of a few other places where I could use one of those. [i.e., something like <.commandjoin> to cancel out <.commandsep>] --------- From Krister Fundin [9/13/06]: > push television into white box (first standing on the main platform) Okay, you're now standing in the white box. Perhaps it's not serious enough that it needs to be fixed now. In the long run, one could imagine a "first trying to push the television onto the main platform" type of implicit action here, but that would take some work. [I'm not even sure what would be right here. I guess some fine-grained variation of PushTravel for pushing into nested rooms or something.] --------- The ThingState mechanism should be extended to allow a single object to have multiple current, active states. For example, it should be possible for an object to be simultaneously worn, lit, and open. As it stands, an object can only have one state at a time. On the parsing side, this is straightforward: we simply have to iterate over the list of active states and consider each one's token selectivity in turn. All active states must rule positively on the token list to allow a match. On the output side, it's slightly more work. We'll have to list multiple states in messages like "(providing light)", which isn't hard: "(worn, open, providing light)". We'll also have to list multiple states in sublists: "one worn, open, and providing light; one closed." This is also relatively straightforward except that we'll have to go to semicolons in the overall list when the sublists start expanding out to the point that they need commas. (Ideally, ThingState probably ought to be the *only* state mechanism for any sort of state for which it's used. For example, there really shouldn't be a separate isOpen property - an object's open/closed status instead ought to be determined by the presence of an openState or closedState object in the object's state vector. Opening an object would then be a matter of calling obj.setState(openState). This would require that each ThingState know its set of states, for mutual exclusion when a new state is set (which would simplify things slightly in that we'd be able to dispense with the per-object allStates list.) (Also ideally, ThingState sets would implicitly and automatically create distinguishers. Any time we have a pair of objects with different active states from a given set, we should automatically be able to distinguish those objects on the basis of that ThingState set.) --------- In announceAmbigActionObject, announceMultiActionObject, and announceDefaultObject, it'd be ideal to use distinguishers to decide on the object name. This would ensure, for example, that we generate "(the lit candle)" when litness would distinguish the candle we chose from others present. I think we'd basically have to do the same work that BasicResolveResults.ambiguousNounPhrase does to choose a distinguisher, but in this case we want to tell apart the object in question from everything else in scope that has the same name. So, probably we want to go through the objects in scope and build a list of everything with the same disambigName; then we want to search for a distinguisher that can tell apart the object in question from all of those other objects; failing that, we could settle for a distinguisher that can tell apart the object in question from any of the other objects, as this would provide at least some greater specificity. Failing that, we just use the disambigName. --------- Consider removing several modules from adv3 and putting them in the "system" library instead: banner.t menusys.t modid.t numbers.t --------- Add a property setting that makes the contents of an item listed immediately after the item's specialDesc, if it's using a specialDesc. The contents would be listed there rather than in the miscellaneous portable list paragraph. --------- Add static File.deleteFile(filename), to delete a file from disk. This operation should require WRITE privileges (as determined by the file safety level setting) for the given file. --------- Add a version of saveGame() and restoreGame() that use a ByteArray as the saved state medium. save() would create and return a new ByteArray object containing the saved state (which must, of course, exclude the new ByteArray itself); restore() would take a ByteArray object and restore its contents as the current game state. For interface purposes, we could simply let saveGame() without an argument return a ByteArray, and restoreGame() could take a ByteArray as its argument. Alternatively, saveGame() could take a pre-created ByteArray as its argument, and we'd fill in that ByteArray with the saved state (but we'd somehow have to make sure that this ByteArray was excluded from the saved state). (Implementation note: this is fairly straightforward, but some work. We first have to refactor CVmFileSave::save() and restore() to take CVmStream objects instead of CVmFile objects; this is fairly simple, but requires changes down through the whole save/restore stacks, which touch a lot of objects. Then, where we currently call save/restore with CVmFile objects, wrap the CVmFile in a CVmFileStream and make the same call. Finally, we'd have to create a new CVmStream subclass that operates on ByteArray objects, which should be a fairly easy mapping. It'd be worth making sure that changing CVmFile to CVmStream in save/restore doesn't balloon up the executable too much - this would introduce a ton of new virtual calls, because all of the CVmStream methods are virtual while the corresponding CVmFile methods are non-virtual. It'd also be worth measuring any degradation in save/restore performance from all the new virtual calls.) --------- Store ALL objects as potential future pronoun antecedents when processing a multi-object command. For example, when executing UNLOCK DOOR WITH KEY, store *both* the door and the key as potential antecedents for 'it'. On using 'it' in a future command, look at all of the possible antecedents, and choose the most logical one - essentially treat the set of possible antecedents as though it were the set of matching objects for an indefinite noun phrase. If there's more than one equally logical choice, choose one arbitrarily, the same way we would if the player typed TAKE ANY BOOK: >unlock door with key Unlocked. >drop it Dropped. // refers to the key, since the door is illogical for DROP ...but if the next command were this instead: >open it Opened. // refers to the door, since the key is illogical for OPEN ...and if it were this instead: >x it (the door) // both door and key are equally logical, so choose It's a massive... // one arbitrarily, and report the choice --------- Debugger: display the current method (the top of the stack trace) in the status line. --------- Debugger: add an option to skip over library code when stepping. This could be a mode, or it could be a new 'step' command (alongside 'step in', 'step over', 'step out'). This would make it easier to isolate a bug in game code, and it would also clarify the control flow through the sequence of game-provided entrypoints. The main question in implementing this is how exactly to define 'library' code for the purposes of this new form of stepping. Library code could be defined as anything from a .tl file, or as any source file under the system directory tree, or as files designated as such manually (with a right-click on a file in the project tree, say). --------- Can we rephrase the UNDO command results so that they reflect the logical command rather than the actual text typed in? Something like: >undo Taking back: attack the troll with the sword (For commands involving multiple objects, this could get cumbersome, since we probably wouldn't be able to easily reconstruct the original ALL or plural usage, but would instead list all of the objects.) --------- Maybe use <.Q> rather than in msg_neu and en_us, to allow customization of quote styles. --------- Add a global compiler options setting to workbench? --------- The compiler should flag some types of errors at the location of the particular token causing the error, rather than at the location of the enclosing source line. Krister Fundin pointed this out with respect to an expression like this, where the 'method' name is undefined: property.method(new function(x) { code; }); The error ought to be reported at the exact line containing the undefined token 'method', rather than at the statement level - which, in this case, reports at the *end* of the nested function. To accomplish this, the compiler would have to (a) save the source location for each individual token, in addition to the location of the current statement; and (b) use the token location rather than the current line when reporting any error that can be associated with a token. [The second part is probably pretty easy - almost all such cases can probably be caught by changing CTcTokenizer::log_error_curtok() to report at the current token position. Saving the token position probably isn't too hard, either, but there might be some subleties, such as with respect to the preprocessor.] --------- Add a general "warning check" mechanism to the library. This would be an add-on module that would encapsulate a bunch of checks for common pitfalls, using reflection mechanisms to display warnings. Authors could optionally compile with this module from time to time to check for common errors. This would only be of interest to authors during development, of course - it would always be removed for the final build for release. (This came up in the context of checking for default Container objects when using the liquid add-on library. It would be nice to have a generic mechanism, or at least some coding patterns to follow, that would simplify adding this kind of warning check.) --------- For ResolvedTopic, it might be nice to ask for disambiguation for in-scope objects if there are more than one, especially if this were parameterized. --------- Menu-based conversation framework --------- For travel, add a check to see if the actor fits through the passage. This should ideally be implemented using a precondition that would check the actor's bulk to ensure it's below a maximum. The actor would have a chance to reduce its bulk via a tryReducingBulk call, perhaps - this would let the actor do something obvious like close an umbrella. --------- For travel, add a check for excessive encumbering bulk or weight. This could be used for things like climbing ropes or walking across a rickety bridge. For excessive encumbrance, the actor could use the standard tryMakingRoomToHold mechanism to reduce held bulk. For excessive weight, this is probably just a connector-specific check and message on failure, so there might not be much we can do in the library other than make sure the framework is in place. --------- Weapons (attack with) --------- flammability as a mix-in (for paper, wood, and the like) --------- Liquids and bottles --------- Drinkables (special kind of liquid) --------- Probably need a variation of objHeld for liquids that lets us specify that we need to be holding the container of the liquid (for 'pour', for example). containerHeld? Or, we could do like we do with keys on keyrings, and consider a liquid within a container being held to be itself held. --------- If we have a bucket or some other container filled with liquid, and we try to put another item into the bucket, we should run a special check for wetting the item. The default should probably just be to say "you don't want to get that wet" and refuse the command. Similarly, "fill bucket" when the bucket contains non-liquid objects should run the same checks. --------- Burning and liquids: wetting a burning item with a non-flammable liquid should extinguish the flame. --------- Burning and liquids: provide a flammable liquid class. This should consume the liquid at a particular rate while burning. The basic flammable fuel consumption mechanism should be used here if possible. --------- Provide an extensible "chemistry" framework for combining different kinds of liquids. Perhaps we could define an object to represent the operation of combining each pair of liquid classes - this class doesn't represent the mixture, but rather the operation of mixing. So, for example, we define an object for the operation of mixing water and liquid nitrogen. Each of mixing object must define a method that is called when its pair of liquids are mixed; this method encapsulates the action of mixing the liquids. Use a global lookup table of these objects, keyed by liquid class pairs; when we want to mix two liquids, we look up the pair of liquid classes to be mixed and find the mixer object, then call its mixer method. --------- Putting a container with liquid into another container in inventory should have some conditions attached. First, an openable liquid container should be closed whenever moving it into a sub-container (so a bottle of water should be closed before putting it in your backpack) to avoid spilling. Second, a container that can't be closed should probably not be allowed to be moved to a sub-container. Third, taking or otherwise moving any item should probably be disallowed if the item contains an open container of liquid. --------- Pouring a liquid onto the floor, into a container that isn't set up as a container for liquids, onto a surface, or onto an arbitrary object should all do something reasonable. It's unclear what to do in general; the easiest thing would be for the liquid evaporate immediately, but that's not all that realistic. Pouring liquid onto an arbitrary object should probably call the object's notify-getting-wet method, just as though the object were in a container being filled (although we might want to differentiate between submerging and dousing, maybe just via a flag to the notify-getting-wet method), but then something needs to happen to the liquid, even if the thing that happens is simply evaporation. --------- Leaky containers: when a leaky container has liquid added, it should set up a daemon to cause it to leak slowly. The liquid leaking out should do whatever liquid does when poured into the leaky object's enclosing location. --------- Absorbent materials: these should retain a quantity of liquid as though they were a liquid container, allowing the absorbed liquid to be recovered by squeezing the object. --------- Ropes, to the extent they can be generalized --------- In two-object actions (including topic and literal actions), when both objects are missing, we might want to consider some alternative way to specify which object we ask for first. This might vary by language, so it might be better to have an action property that specifies which goes first when both are missing. Or maybe this is the same as resolution order, or opposite of the resolution order, or something. --------- OBJECTS command - list all portable objects that have been seen, and the last location where they were seen --------- It seems like it should be possible to talk to an actor in situations when the actor is audible only, such as in the dark. But how do we initiate contact if we don't see the actor? 1 - broadcast a greeting: >hello Bob says, "Is that you? I can't see anything." 2 - try talking to a specific actor: >bob, hello This doesn't seem very good because we need to have a way of knowing Bob is present in order to talk to him. 3 - the actor hears you come in and says he's present: >n In the Dark It's pitch black. In the darkness, you hear Bob. "Is that you?" he asks. --------- Add new syntax: x.delegated y.prop(args) This syntax explicitly targets 'y' with self as 'x'. This differs from the regular 'delegated y.prop()' in that the latter can only set 'self' in the delgatee to 'self' in the delegating method. Or, perhaps we could take this one step further, by allowing the target object, self, *and* defining object to be independently specified. Something like x.delegated y.inherited z.prop(args) This would set self to x, target object to y, and defining object to z, then try to find the method in z. If the method doesn't exist in z, we should probably throw an error rather than try to inherit from there, since this is so fully specified already. We would need a new opcode for each case. The first would require an opcode with the new self, the target object, and the target property as operands; the second would require those operands plus another for the defining object. We could have two variations of each opcode (one with a fixed property ID and the other with a property pointer operand from the stack), or we could presume that the operands would be rarely used and define only the property pointer version (it would be less efficient for cases with a static property ID, but if the opcode is rarely used, this extra cost shouldn't be important). --------- Add a Set intrinsic class? (For unordered lists, optimized for inclusion testing: essentially a LookupTable with only the keys.) --------- Soft linking: local x = ifdef(obj); // if obj is linked in, return obj, else nil replace ifdef obj ... // same as a new object def if obj isn't linked in modify ifdef obj ... // ignored if obj isn't linked in --------- List methods: add compare funcs to more methods: - intersect - getUnique - appendUnique likewise: - Array.getUnique - Array.appendUnique - Vector.getUnique - Vector.appendUnique --------- Compiler: equivalent property list syntax: a, b = foo c, d(x) { bar } --------- Add items that are by themselves ambiguous? This might be interesting for things like PO Boxes: >look in box Which box do you mean, the cardboard box, or a mail box? >mail box Which mail box do you mean? They're numbered from 23000 to 23999. >23001 Box 23001 seems to be locked. This could be accomplished with an isAmbiguous flag in the object, which would be similar in effect to isEquivalent. Whenever the object shows up in a list with multiple objects, we use an indefinite article with it, as though it were one of a bunch of equivalent items. When the object shows up by itself, we ask a single-object disambig question, perhaps by calling a method on the object itself - disambigPrompt? Ultimately, matchName() and matchNameDisambig() would be responsible for coming up with a specific object based on the noun phrase. --------- repeated messages: gather up and group defaults? --------- Sound defaults. Add a background sound resource property to each object. On each turn, the library scans for all objects in hearing range and plays the associated background sound for each one. Could have near/far/obscured versions, or maybe simply adjust the volume, or whatever. --------- Graphics defaults. Add a graphic resource property to each room and item. On "look" and "examine", display the associated graphic as part of the default room/item description. It would be especially nice to have a choice of presentation styles, maybe even including a Legend-style split-screen interface (but that might be more appropriate as an add-on; even so, it would be good to keep it in mind so that the necessary hooks are there for creating a Legend-style presentation add-on). --------- Add a music soundtrack scheme that simplifies management of background music. --------- Look into formally structuring things into try/check method pairs (checkHolding/tryHolding, etc), to make precondition implementation more consistent and clear. This would apply most to the preconditions. --------- Krister Fundin proposes: The list of end-of-game options currently specified as the 'extra' parameter to the finishGame() and finishGameMsg() functions could instead be bundled up into a FinishType object, so that the message and extra options could be specified with just one object parameter. This would make game-ending code more concise and also make it easier to reuse an option list. [We'd probably need a new common function that takes a FinishType object as its sole parameter; the existing interface functions would simply create a dynamic FinishType object to encapsulate the parameters and call the new common function.] --------- Krister Fundin proposes allowing a list parameter in the middle of a parameter list, rather than only as the last parameter, as in foo(a, [b], c). The compiler could generate function prologue code that moves the trailing arguments into locals, then truncates the list created via PUSHPARLST. It's a little inefficient, since we just throw away the first list and have to move the locals around, but the case is probably sufficiently rare and non-critical that this doesn't much matter. --------- Andreas Sewe <2003-10-01.as.int-fiction@web.de> proposes that 'inherited' in templates should NOT implicitly expand to nothing. As it is, 'inherited' in a template expands to each inherited superclass template, but also expands to nothing. Andreas suggests that this should be changed to eliminate the empty expansion, AND the empty template should be allowed, so that it's possible to make inheritors explicitly pick up the empty case. [It's not entirely clear to me that the empty case really needs to be inherited in the first place, but Andreas's point is that it *could* be there to ensure backwards compatibility with the existing template structure, which implicitly does inherit the empty case.] --------- From <2003-10-01.as.int-fiction@web.de>: add a way of naming anonymous functions (which would make the name an oxymoron, so I suppose we'd have to rename them to "in-line" functions or similar). Something like this: local f = new function factorial(n) { return n > 1 ? n*factorial(n-1) : 1; }; --------- From Krister Fundin : add TadsObject.forEachSuperclass(); takes a callback function pointer as its argument. This would iterate through all of the direct and indirect superclasses of an object. (This is readily implementable in byte code, but the VM might have the class hierarchy cached, so it could iterate through them more efficiently than byte code could.) --------- From Robey Holderith : add a new Python-style string notation: use three single quotes in a row as an alternative single-quoted string delimiter. For example: '''Sorry, she's not here right now.''' would be treated as equivalent to 'Sorry, she\'s not here right now.' Many people find the Python notation more readable than the present backslash notation. The backslash notation would be retained as well; this would just be a new, optional variation. (There's some compatibility risk, but it's probably negligible - it's hard to come up with any example of where a tripled quote like this would be useful in the present notation.) --------- From steve breslin : Add auto-completion popup lists and parameter-list tooltips to the editor, a la "intellisense" in msft dev env's. Scintilla has native support for both of these (the "autocomplete" and "call tips" features), so there'd be no UI work; it's just a matter of monitoring keystrokes, generating the popup/tip data, and making the appropriate SCI_xxx calls to activate the features. The main difficulty is that TADS is not a statically typed language, so it's not possible in principal to generate the popup data that you can in a language like C++, where you can determine the type of any expression at compile time (and thus at editing time). You could find the popup information only when referring directly to an object by name. I'm not sure if the result would be useful enough to justify the work. The other complication is that I don't think the compiler currently provides any sort of convenient packaging for the sort of information you'd need to generate the popups; it would probably require some compiler work to generate a little database giving the method list for each object. This wouldn't be a ton of work but wouldn't be free. --------- From Knight Errant : add a spell-checker to Workbench. (Could be an on-the-fly spell checker a la Word's squiggly underlines, or could be an explicit separate step. I think Scintilla has some built-in support for the display part of on-the-fly spell checking, so that would probably be the way to go. Spell checking would be most useful if it were mode-sensitive, so that, for example, the tads3 mode would only check text within strings, and exclude HTML substrings. Presumably we could use the style information to do this.) --------- From Eric Eve : It would be quite nice if TravelBarrier objects had some easy way to refer to the connector through which they're blocking travel in their explainTravelBarrier() method. One way to do this might be to add a second parameter to TravelBarrier.explainTravelBarrier and then have TravelConnector.checkTravelBarriers() call explainTravelBarrier(traveler, self) instead of explainTravelBarrier(traveler); but this could break a lot of existing code. The slightly messier alternative, which might nevertheless better preserve backward compatibility would be to add a connector property to TravelBarrier and then add cur.connector = self just before cur.explainTravelBarrier(traveler) in the foreach(cur in lst) loop in TravelConnector.checkTravelBarriers(). --------- From Eric Eve : At present attempting to travel via an AskConnector will always prefer an open door to a closed one (without prompting the player to specify which door s/he wants to go through). This may be fine as default behaviour but sometimes in might be nice to allow the option of always prompting the player to specify which door is meant. (I can see that this might be quite fiddly to achieve, and if it's too fiddly then by all means don't pursue it; it's hardly high priority). --------- From Eric Eve : IIRC this came up before, but I can't remember that it was ever pursued. It would be quite nice if ThingMatchTopic could match on a class as well as an object, e.g.: ThingMatchTopic.matchTopic(fromActor, obj) { /* * if matchObj is a collection, check each element, otherwise * just match the single object */ if (matchObj.ofKind(Collection)) { /* try matching each object in the list */ if (matchObj.indexWhich({cur: obj.ofKind(cur)}) != nil) return matchScore; } else { /* match the single object */ if (obj.ofKind(matchObj)) return matchScore; } /* didn't find a match - indicate this by returning a nil score */ return nil; } With corresponding changes to ThingMatchTopic.isMatchPossible(). So far as I can see this shouldn't break any existing code (if obj==matchObj then obj.ofKind(matchObj)), but would allow for greater flexibility, making it easier to write responses such as: + GiveShowTopic @Food topicResponse() { gDobj.moveInto(nil); "Bob snatches {the dobj/him} from you and wolfs it down.\b Thanks, he says, I needed that -- I waa starving!"; } ; --------- From Eric Eve : Some time ago Michel sent me some very nice code to add a vocabLikehihood property to VocabObject, which allows authors to nudge the parser to prefering some objects to others when all other distinguishers fail. I've found it pretty useful ever since and I think it would make a nice addition to the standard library if Michel were willing to contribute it.