custom message IDs, adv3Lite

One of several reasons I was drawn to adv3lite as opposed to adv3 so early in my TADS3 exposure was the promise of easily customizable system text via the overriding of messages in a CustomMessage object, as opposed to what I perceived to be a relatively steep learning curve for mastery of the adv3 transcript.

No offense to the developers of the authoring system :slight_smile: but as a writer, I want to be the one who decides what words do or don’t show up in the presentation of the material.

In Lite, it’s as simple as…


messages = 
    Msg(currently no hints, 'What? Already?  No way. Try some stuff first,
        then ask for help. '),


To do so, however, requires that you know the key, or ID, that uniquely identifies the message you want to override—the currently no hints part of the Msg redefinition quoted above.

Where to get those keys? And how to know exactly, in advance, what system text may be a candidate for an override?

The Messages chapter of Part IV: Actions of the adv3Lite Library Manual says the system defines messages as either of two types: BMsg or DMsg.

The currently no hints message is defined in hintsys.t as…

        /* if there are no hints available, say so and give up */
        if (topHintMenuObj.contents.length() == 0)
            DMsg(currently no hints, '<.parser>Sorry, no hints are currently
                available. Please check back later.<./parser> ');

How would I have known that if someone hadn’t already told me to use the currently no hints ID?

That was the question I set out to answer.

I started by grepping for BMsg and DMsg in the directory that contains all .t files of the Lite library. That worked, but grep output to a command window is a bit cumbersome to read, especially when there are a combined total of more than 500 messages of both types. So I started tinkering in Eclipse and came up with a utility that reads all of the source files and writes all message definitions to a CSV file, that can be opened in a spreadsheet application like OpenOffice or Excel where I can search and sort the data to locate what I need when I need it.

For anyone interested, the CSV file is attached.

Caveat—because of the many different ways a line of code can be presented—one line, multiple lines, some definitions end with ') some with just ) and so on and so forth—my utility was not always able to extract the complete definition.

But most of the time, the essential data needed for customization is in the CSV file, and if not, there should be enough information to at least locate the place in the code where more information resides.

tads3lite_msgs.csv (63.8 KB)

I had it in the back of my mind that something like this would be needed at some stage, so thanks for coming up with this! I was wondering how the information gleaned from this sort of exercise might be usefully presented, but putting it in a spreadsheet file might well be a good solution, for the reasons you state.

Just after uploading the CSV, I had second thoughts about that caveat about multiple-line text in the code.

I fired up Eclipse again and took a second look and, sure enough, I think I found a better way. I can in fact detect when a line break intervenes in the midst of the text, and the CSV generator is now able to construct a complete message.

A revised CSV is attached.

tads3lite_msgs.csv (78.3 KB)


This will also be a useful tool for me to go through and check all the messages. A quick look at your list suggests there are some duplicates (in some cases because the same message is needed in more than one place, but I’d like to tidy up in the library) and at least in one case because it looks like I’ve used the same message ID for two different messages. Now I can easily sort the list by message ID in a spreadsheet I can easily check for this sort of thing.

If it’s okay with you, I’d quite like to include this with the documentation set in future releases, but we’d first need to devise a way of updating the list from the current set of files just prior to release.

Yes, by all means. Not a problem.

Also not a problem. I’ve attached to this post the discovery tool which you can run whenever you want on your set of library files.

There is a readme inside the ZIP that provides usage instructions. If you encounter any difficulties, please let me know and I’ll update the utility as necessary. (I haven’t done a lot of QA on it, and it is possible that it will behave differently once it gets outside of my environment. Be sure to edit the properties file, for one thing; it directs output to my preferred target, in my filesystem. Yours may be different. The utility also does not have a very robust internal error-trapping system, so there may be some obtuse error messages in the form of Java stack traces if unforeseen problems arise.)

I have also encountered a couple of anomalies while scanning the CSV.

In parser.t, a none in location DMsg is defined as…

             *   We have a locational qualifier, and we have exactly one
             *   object that matches the location, but there's nothing in
             *   scope that matches the main noun phrase that's in that
             *   location.  For example, the player typed THE BOX ON THE
             *   TABLE, and we have a table in scope, but there's no box on
             *   it. 
            DMsg(none in location,
                 '{I} {see} no {2} {3} {the 4}.',
                 cmd, txt, locQual.locType.prep, locQual.matches[1].obj);

In my test bed environment, I have a table in a room with nothing else there. When I enter Put box on table, I get…

When I override none in location…

…nothing changes. The game response is unchanged. To change the message, I need to override the unmatched noun message.


The second problem with this exercise is the substitution of the noun as entered in the game for {2} in the message.

If I enter “put box on table” it works okay, but if I enter “put the box on the table” the message echos “the box”. This is true in the default DMsg as well as my override.

Here’s the output with the default message in place…

Jerry (13.7 KB)

Thanks for tads3Lite-discovery tool. I’ll try it out in due course and let you know if I get into any problems with it.

I believe the behaviour you’re seeing with the ‘none in location’ message is as it should be. The ‘none in location’ message is the one that should be used when a command uses a locational qualifier that doesn’t match any object, e.g. EXAMINE BOX ON TABLE when there’s no box on the table. (In this example the player may have been trying to distinguish the box on the table from the box on the floor or the box on the shelf). In the case of PUT BOX ON TABLE, ON TABLE isn’t a locational qualifier (it’s not answering the question, ‘which box do you mean’) but a phrase defining the indirect object of the PUT ON command, for which the unmatched noun message is the appropriate one to use if there’s no box in scope.

Again I’m not 100% that the behaviour of the ‘unmatched noun’ message that you describe is actually wrong, since its function is simply to echo back to the player the noun phrase that the parser failed to match to an object in scope. I’ll take a look at it, but I suspect it may not be worth fixing (even assuming there’s really something here to fix).

Yes, I see it now, you are correct. If I put a box on the floor in the room and then say Examine box on table, I get my none in location override message.

Maybe not from your perspective, but to the person reading it, it almost certainly will be perceived as incorrect grammar. And since most other text handling parts of tads3 makes a concerted effort to get parts of speech—articles, prepositions, and whatnots—correct, this seems out of place.

Well, that’s a judgment call best left to the person whose the time will required to do the fixing. :slight_smile:

If there are other things higher up on the priority list, so be it. In the grand scheme of things, this is a fairly minor grammatical transgression, especially since it will only be displayed on rare occasions.


I take your point about the incorrect grammar. I had to cut short my explanation in my last post as I was called away to do something else. My main concern was not to introduce something into the DMsg() string that would be too language-dependent (and so hinder translating the library), but now I’ve had a chance to think about it I think I’ve come up with a way to handle it that seems to work okay.

In case you’re interested in trying it out, first change the DMsg in question so it reads:

DMsg(unmatched noun, '{I} {see} no {2} {here}.', cmd, stripArticle(txt));

Then define the following function in english.t:

 *   Remove any definite or indefinite article that occurs at the beginning of
 *   txt, and return the resultant string in lower case.
    txt = txt.toLower();
    txt = txt.findReplace(R'^(the|a|an|some) ','');
    return txt;

I think this would be okay for any future translators, since they could provide their own version of stripArticle() in their own language-specific file, e.g. a hypothetical future francais.t might define:

    txt = txt.toLower();
    txt = txt.findReplace(R'^(le|la|les|un|une|des) ','');
    return txt;

Or whatever seemed appropriate to the French version. This separates the language-dependent part from parser.t, which was my main concern here.

Okay, great! tried it out, seems to be working! Thanks a lot.


Thanks, but unfortunately I can’t get it to work. When I try to run it according to your instructions I just get a long error message, ending with “Could not find the main class: Program will exit.”

Incidentally I get a similar error message (apart from the name of the missing class) with the indexing tool.

Sorry about that. Packaging error. There is a Manifest file inside the .jar that should point to the main class; that file was malformed in the discover ZIP. I’ve corrected that, and tested it by exporting the ZIP to a different system and running it, to be sure there isn’t some dependency on my development environment.

Download the new ZIP attached here and try it again.

Not sure about what’s going on with the indexer, though. The manifest file in that ZIP is correctly formed, and when I export the ZIP to a different system, it works fine for me. Nonetheless, I have rezipped it and attached a new zip that corrects a couple of other packaging errors.

If you don’t mind trying again, download the new indexer zip and run it. If you still get an error, please send me the .log file that should be written to the resources folder.

If the utility fails before that file can get written, it would be valuable for me to see the complete error message. If you just run the .bat from the Windows Explorer, it won’t stick around long enough to capture. But if you open a command window (Start button, Accessories folder, Command Prompt icon) then relocate to the location of the expanded zip and enter the following on the command line…

tads3lite-indexer > indexer.log

…the “> indexer.log” part will cause the console output to be written to a file named indexer.log. I’d like to see the contents of that file.

Jerry (13.9 KB) (13.9 KB)

Another thing to look for is which version of Java you have installed on your machine.

If you have not updated Java for a while, you may not have the version needed to run these utilities. You should have Java 7 (current released version is update 21, though update level is not as important as being Java 7 as opposed to Java 6).

Run the following on a command line…

…to check your Java version. It should say Java 1.7 (which is Java-speak for Java 7).

Okay, I’ve now got it all working (both the indexer and the DMsg() discovery tool).

It turns out Windows 7 had a few more tricks up its sleeve.

Running java -version reported that I had version 1.6 as you supposed.

On downloading and installing the latest version of java 1.7 the installer told me I already had it, but I went ahead and installed it anyway.

Running java -version then still reported that I had version 1.6.

I managed to figure out what was going on: running java from the command prompt invokes the 64 bit version of java, while downloading the recommended update from the java website downloads and installs the 32 bit version. When I explicitly looked for and downloaded the 64 bit version (as well) all was well.

Anyway, it’s all working okay now.

I’m looking into adding a Messages index to the adv3Lite Library Reference Manual. I have the source code for the docgen tool that generates the LRM from the library source files, and I’ve already tweaked it to work better with adv3Lite, so I may be able to tweak it a bit further to index BMsg() and DMsg() definitions too. I’m not making any firm promises just yet, but I have made a start on trying to figure out how to do it.

Great. Glad to hear you got the Java tools working. I know, getting Java and Microsoft to work together can be…how shall I say? Interesting.

If you encounter any more difficulties using the tools I provided, let me know. I’m happy to sort out any problems that may arise, or even tweak to specifications if something works but not quite as you’d like.

But if you can get your own tool to do the work, that’s even better, you can tweak it however you like.

I know I’m only one voice in the wind, but I do hope you’re able to integrate some sort of road map to the message IDs as part of the official docset, and I also hope you can devise some way of indexing individual docs. But I also understand the writing process, what’s reasonable and feasible to do you do and what isn’t you don’t :slight_smile:

Thanks for considering the suggestions offered.


I’ve now got this working; that is the adv3Lite Library Reference Manual now includes an index to message IDs that lists them in alphabetical order and links to where they’re defined in the source code. I’ll probably also include a csv file generated by your tool in case people find that easier to work with (since they can search, sort and otherwise manipulate a spreadsheet any way they like).

I have some ideas on that which I’m planning to start trying out later today. I don’t want to start promising any vapourware at this point, but I think it may be possible to write a tool to do this in TADS 3 itself (it looks to me as if it should have the relevant capabilities), which would then make it easy to use for anyone who already has TADS 3. I also want to experiment with an idea I have to avoid having to add a huge number of extra anchor tags to the documentation files in order to index them (though some extra tags will also be necessary).

I’ve now had a go at writing a Manual indexing tool (that’s a tool for indexing the manual, not a tool for indexing by hand!), and the result is attached in case anyone wants to give it a go. It could doubtless do with quite a bit of polish, but it seems to be doing more or less what I envisaged.

To use it, you need to unzip it into your adv3Lite directory (or, if you’re nervous, into a copy thereof that at least contains the manual directory underneath it). Replace the toc.htm file in the manual directory with the version in the attached zip file (this just adds a link to the index). Then run the docindex.t3 file from the adv3Lite directory (or wherever you’ve put your copy of the manual directory). docindex.t3 needs to be run from there to have the necessary file permissions; as its file suffix suggests, docindex.t3 is a TADS 3 executable (it needs the TADS 3 VM to run, and runs in a T3 terp).

The result should be that a new manual_idx.html file appears in your manual directory. This contains the index to which the replacement version of toc.htm should provide a link (so that you can access the index from the table of contents). This should work without your having to add a single anchor tag to the existing manual files (though I shall need to add several anchor tags before the next release to get the best out of this tool).

docindex.t3 works by creating an index of the following:

  1. All the headings in (most of) the manual files (i.e. heading marked with tags).
  2. Anything (with some deliberate exceptions) that appears in bold (i.e. anything between and or and tags).
  3. Anything marked by Jerry Ford’s indexing tags (i.e. anchor name tags with names ending in ‘idx’).

The rationale behind (2) is that the manual tends to place terms in bold when it introduces them for the first time. Picking out terms in bold is thus a pretty good way of picking out the names of methods, properties and classes that are useful to index. The tool also excludes items in bold where they occur in certain contexts that would make them clearly inappropriate for indexing (such as code samples or sample transcripts). There are also several files it skips completely, such as the various table-of-contents files and the change log.

The results seem to me to be reasonably good. Of course the tool is picking up one or two things that shouldn’t be indexed and no doubt missing a few things that arguably should, but on the whole it seems to me to be producing a pretty comprehensive index without the need to insert a whole of anchor tags by hand.

As this stands this may also manifest itself as a bit of a limitation, since the tool is obliged to use the most recently-defined anchor for any item to provide its link from the index, and in some cases that may be quite some way up the file. That’s why I’ll need to add some more anchor tags to the manual files in due course; but at least I won’t have to add anything like as many anchor tags as there are items in the index.

Other than for the purposes of experimenting, it’s probably not worth anyone else adding a whole lot of anchor tags to their copy of the manual files, since they’ll only get overridden by the next of adv3Lite.

Edit: I’ve now made a few more tweaks to the docindex tool and have attached the updated version. (12.9 KB)