TADS Programming - Ask/Tell - Part 1 asktell.t: ASK/TELL-based conversation system for TADS V1.0 Suzanne Skinner, 1999, Public Domain tril@igs.net This file implements a topic-based ask/tell system similar to that used by WorldClass. It also implements information sources (i.e. things that let you look up information), since they are a simple and obvious addition. It requires TADS 2.4 at minimum (since it uses 2.4's new disambiguation hook, disambigXobj). Preferably, you should use a patched or later version of TADS, since 2.4 has a glitch which will cause disambiguation questions to look odd. To use asktell.t, include it after adv.t in your main source file. See the comments in the source below, especially for the superclasses topic, ioTopicVerb, and movableActor, to learn how to implement conversations using this library. This file uses #pragma C+, but sets back to #pragma C- at the end. Features: + Full disambiguation: If you "ask so-and-so about tree", and there are two different trees you might be asking about, the game will respond with a disambiguation question: just as with other verbs. + Topic-based: Indirect objects for ASK/TELL are scoped to allow topics only. Non-topic objects will never show up in disambiguation questions. + Knowledge-based: A topic may be known or unknown at a given time. Unknown topics will also never show up in disambiguation questions. + Mimesis-preserving: If the player uses vocabulary that doesn't match any topics, the NPC's "disavow" property will be output, instead of a cryptic message such as "something tells you that won't be a very productive topic". disambigXobj allows us to accomplish this. Tips: + Be careful of unknown topics. If you have a topic that can be referred to by a very general noun, e.g. "enchanted tree" (which can be called simply "tree" by the player), it may be best to make it a knownTopic. Or, make sure there is a general known topic that matches that noun from the start (e.g. a "forest" object). Otherwise, the player may get peeved by the seemingly erroneous "you don't know about that" message. + WorldClass sets up carryable items to be topics by default. I recommend against this, at least in a large game (in my own game, there are often 3-4 different versions of the same item floating around). Keep the topic system separate to help insure that no impossible disambiguation questions will show up ("Which ball do you mean, the ball, the ball, or the ball?"). + This library assumes that only topics draw a distinction between known and unknown. But if you prefer a WCish world in which anything can be unknown (and therefore, "ask about [unknown non-topic]" should result in "you don't know about that"), the changes shouldn't be hard to make. Just fiddle with disambigIobj. + Scenario: You have in your game a red gem and a green sword. But nowhere does there exist a green gem. What happens when the player types "ask so-and-so about green gem"? This: I don't see any green gem here. This happens in any TADS implementation of ASK/TELL. It is the catchall error message displayed when a non-existent noun/adjective combination is used. This is an artifact of how TADS is set up internally: it favors local-scope verbs, for which that error does make sense. You can change the message (#9) using parseError to say something more meaningful (e.g. "I don't know of any such thing"), but there are complications: the same error message is used for other situations of a different nature. Also, there is no practical way (that I know of) to determine what verb was used in this case. It is handled too early in the parsing process. It's possible to fix this somewhat, but tricky. I won't go into details here, but if enough people ask, I can add the solution into this source file. #pragma C+ / New Superclasses / topic: something which can be asked about, told about, or looked up in information sources. The code for scoping and disambiguation with topics is largely contained in ioTopicVerb. Important properties: + known: indicates whether the player currently knows about this topic. If nil, it will never show up in disambiguation questions, and the player cannot get information by asking about it. + unknownMsg: the message printed when the player's vocabulary matches only unknown topic(s). class topic: thing known = nil unknownMsg = "You don't know about that." location = nil ; knownTopic: a topic which is known from the beginning. Important properties: none class knownTopic: topic known = true ; infoSource: something the player can look things up in. Important properties: + askTopics(topic, words): This works the same as askTopics in movableActor. "topic" is the topic asked about, and "words" are the vocabulary words that were used to refer to it. The method should output text and return true if the infoSource has an entry for that topic, otherwise return nil. This method will never be called with an unknown topic. + disavow: This method will be printed whenever askTopics returns nil. class infoSource: thing askTopics(topic, words) = {return nil;} disavow = "There's no entry for that topic." verIoLookupIn(actor) = { if (self.location != Me) "You're not holding <>."; } ioLookupIn(actor, dobj) = { We have to handleCatchallUnknownTopic here, unlike with other verbs: if (dobj == catchallUnknownTopic) topic.unknownMsg; else if (!self.askTopics(dobj, objwords(1))) self.disavow; } verDoConsultOn(actor, io) = {self.verIoLookupIn(actor);} doConsultOn(actor, io) = { if (!self.askTopics(io, objwords(2))) self.disavow; } ; @~To be concluded next issue - o -