Eric Eve has provided a wonderful conversational extention for users of Inform 7. Called “Conversation Package“, it uses a number of his other extensions to wrap up as full a TADS 3 conversation implementation as he could put together. If you are curious as to why that’s so great, I encourage you to read the essay by Mike Roberts, the author of TADS 3, that covers his analysis of conversation methods in interactive fiction. I’m pretty thoroughly convinced by his arguments that an ask/tell conversation system with topic prompting is the optimal way to implement conversation in a game where it’s going to be more than trivially important to the gameplay, and where there’s no pressing reason to handle it differently.
Likewise, Aaron Reed, author of Blue Lacuna, has published an Inform 7 extension called Keyword Interface. Blue Lacuna uses a very user-friendly system whereby the user can perform certain actions by simply typing highlighted keywords — specifically: examining items, moving to different locations, and selecting topics in conversation. The Keyword Interface extension provides drop-in support for examining and moving as in Blue Lacuna, but requires significant user implementation to make topic keyword highlighting work in your game.
In this article I’ll detail the steps necessary to make Aaron’s topic keyword highlighting work in the context of a Conversation Package implementation. I’m not addressing what you would need to do to get one-word keyword conversations actually functional (mainly as I haven’t gotten around to that yet — when I do, I’ll post it) — this is just to get the highlighting working.
With that said, let’s get started!
The first thing to do, as always, is to Read the Fine Manual — in this case, we’ll start with the documentation for Keyword Interface, where we discover that Aaron Reed provides the [t] and [x] styling tags to support topic keyword highlighting. Great! The next thing we do is to dig into the Conversation Package extension family to see whether there’s a way to inject the [t] and [x] tags into the topic listing code.
Reading the documentation for Conversation Suggestions we find that we can change the way suggestion names are printed while listing conversational topics using a rule. Here’s the way I did it:
Rule for printing the name of something (called item) when listing suggested topics: say "[t][printed name of the item][x]";
This rule says that whenever we are printing a name for something, and we’re doing it while we’re listing topics, we should follow this rule, which will bracket the name of the item with Aaron’s styling tags. You might be asking why I wrote “[printed name of the item]” rather than just “[item]” in this rule. The reason is that when you use “[item]“, the “printing the name of” activity is invoked. Since this rule itself modifies the “printing the name of” activity, this would lead to an infinite loop that would crash your game at run-time. Instead, we use the printed name property, which doesn’t invoke the “printing the name of” activity, and we’re fine.
Except we’re not fine. It doesn’t work. The topic list prints out exactly like before — our formatting tags have been eaten! The documentation is not particularly helpful here, but what we find when we get into the source code is that there’s an action rule declared in Conversation Suggestions — “carry out listing suggested topics” — that does the heavy lifting for printing out the suggested topic list. That code is reproduced below:
Carry out listing suggested topics:
consider the suggestion list construction rules;
let ask-suggs be the number of entries in sugg-list-ask;
let tell-suggs be the number of entries in sugg-list-tell;
let other-suggs be the number of entries in sugg-list-other;
if ask-suggs + tell-suggs + other-suggs is 0 begin;
say "[nothing specific]";
rule succeeds;
end if;
let sugg-rep be an indexed text;
say "[if topic-request is implicit]([end if]You could ";
if other-suggs > 0 begin;
let sugg-rep be "[sugg-list-other]";
replace the regular expression "\band\b" in sugg-rep with "or";
say "[sugg-rep][if tell-suggs + ask-suggs > 0]; or [end if]";
end if;
if ask-suggs > 0 begin;
let sugg-rep be "[sugg-list-ask with definite articles]";
replace the regular expression "\band\b" in sugg-rep with "or";
say "ask [it-them of the current interlocutor] about [sugg-rep][if tell-suggs > 0]; or [end if]";
end if;
if tell-suggs > 0 begin;
let sugg-rep be "[sugg-list-tell with definite articles]";
replace the regular expression "\band\b" in sugg-rep with "or";
say "tell [it-them of the current interlocutor] about [sugg-rep]";
end if;
say "[if topic-request is implicit].)[paragraph break][otherwise].[end if]"What’s going on here is that Eric has 3 different suggestion lists. One is for ask suggestions, one for tell suggestions, and one for any other suggestions that aren’t either asking or telling something. Each of these lists needs to be printed, and each list’s output is separated from the next using a semicolon.
The normal way Inform prints a list is to conjoin the entries using “and”. For example:
let L1 be {2, 3, 5, 7, 11};
say L1;The above will, by default, produce the text “2, 3, 5, 7 and 11″.
Eric wants to use “or” instead, so he buffers the standard list output into indexed text, and then performs a regular expression replacement on it to turn ‘and’ into ‘or’. This is repeated three times, and the resultant output is assembled from the converted indexed text. It’s pretty straightforward.
The problem with this is that when the list output is buffered into indexed text, you lose all formatting — the only thing indexed text can hold is text. The indexed text is also atomic — indivisible — you can style the whole thing or nothing. So even if we had a way to inject the appropriate tags into the string, they’d just print out literally — we’re stuck, or so it seems!
If there were a way to avoid the indexed text conversion and print the output directly using one or more standard “say” statements, we could inject the tags using our rule and get the formatting we want. And since the indexed text trick is used solely to change ‘and’ to ‘or’ in the list output, if we could identify a good alternative way to do that textual modification that didn’t require the indexed text trick, we could eliminate it with a clear conscience.
Emily Short to the rescue! Or at least, Emily Short’s Complex Listing extension to the rescue. Complex Listing is built into the latest distribution of Inform 7, and it is used to modify the way list output is formatted. One of the built in list styles in that extension, actually, is “disjunctive” style, which outputs lists with ‘or’ instead of ‘and’ — exactly what we were looking for! As a bonus, Complex Listing does not use indexed text tricks — you can look up the code if you want the details, but it uses a series of “say” statements instead, so we’re good on that front.
Using Complex Printing requires that we jump through a few extra syntactic hoops — you have to “prepare” a list for printing, and then output it using a particular type of “say” statement, like so:
Instead of taking inventory:
prepare a list of things carried by the player;
say "Your possessions are few and small -- in your hands [is-are a prepared list delimited in dashed style]."In the Complex Listing extension, “in dashed style” refers to one of the styles defined by the extension, or potentially by the author. To implement our topic keyword listing, we are just going to use the stock disjunctive style, along with our custom “printing the name of” rule to inject the formatting. The new logic is then invoked using a replacement for the original “carry out listing suggested topics” rule, like so:
Carry out listing suggested topics: consider the suggestion list construction rules; let ask-suggs be the number of entries in sugg-list-ask; let tell-suggs be the number of entries in sugg-list-tell; let other-suggs be the number of entries in sugg-list-other; if ask-suggs + tell-suggs + other-suggs is 0: say "[nothing specific]"; rule succeeds; say "[if topic-request is implicit]([end if]You could "; if other-suggs > 0: say "[sugg-list-other in topic format][if tell-suggs + ask-suggs > 0]; or [end if]"; if ask-suggs > 0: say "ask [it-them of the current interlocutor] about [sugg-list-ask in topic format][if tell-suggs > 0]; or [end if]"; if tell-suggs > 0: say "tell [it-them of the current interlocutor] about [sugg-list-tell in topic format]"; say "[if topic-request is implicit].)[paragraph break][otherwise].[end if]"; To say (l - a list of objects) in topic format: set up l for topic printing; say "[the prepared list delimited in disjunctive style]"; To set up (l - a list of objects) for topic printing: repeat with item running through l: now the item is marked for special listing; register things marked for listing.
Note the “now the item is marked for special listing” loop. I was unable to come up with a working description for “items in l”, otherwise I would have been able to use a “prepare a list of…” command. Fortunately, Emily explains an alternative mechanism for constructing such a list (setting the special listing property on the desired items, then using “register things marked for listing”) so we can do something equivalent, if a bit more wordy. The effect is the same — we are using Complex Listing to prepare and print the lists without resorting to indexed text buffering, so any formatting we use is preserved. Now when we ask for a list of topic suggestions (or when one prints automatically) the topic keywords will automatically show up in the appropriate topic keyword highlighting.
Next stop: implementing single-word topic commands during conversation. Once I work through this myself, I’ll post a guide for it as well. I hope this is useful to any developers trying to combine Aaron’s and Eric’s excellent extensions!

#1 by Chuck Smith on December 21st, 2009
| Quote
This is exactly what I am doing in my IF. So I will take a look at the code above and your future entries.
What I am doing now seems to work fine. I have only one conversation though.
Chuck
#2 by Matt Wigdahl on December 21st, 2009
| Quote
Great! Hope it is of use to you!
#3 by Eric Eve on December 22nd, 2009
| Quote
Thanks for this. I couldn’t quite get it to compile as written; I think the problem is that the line:
say “[the prepared list delimited in disjunctive topic style]“;
Should be
say “[the prepared list delimited in disjunctive style]“;
If you’re happy for me to do so I’ll incorporate your code (with that one correction) into the next version of Conversation Suggestions so that it’s used automatically whenever Complex Listing is included in the project.
– Eric
#4 by Matt Wigdahl on December 22nd, 2009
| Quote
Absolutely, that would be great! Tomorrow will have additional corrections to that code; in the process of working up the single-keyword conversational topics I found an improvement that hews closer to the behavior Aaron implements for object keywords.
#5 by Matt Wigdahl on December 22nd, 2009
| Quote
I just corrected the error you found in this post as well. Thanks again!
Pingback: Inform 7 Development: Implementing Single-Keyword Conversations « The Quern
Pingback: Keyword Interface for Conversation Package « The Quern
Pingback: Inform 7 Tips — Keyword Disambiguation For Verb/Noun Conflicts « The Quern