| /* 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 | |
| * 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 <<self.thedesc>>."; | |
| } | |
| ioLookupIn(actor, dobj) = { | |
| // We have to handle catchallUnknownTopic 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; | |
| } | |
| ; | |
| // ioTopicVerb: a verb which uses topics as indirect objects. Non-topics | |
| // will never show up in disambiguation questions. validIo and validIoList | |
| // are not used for scoping, nor are verIoAskAbout and the like used. | |
| // Instead, all disambiguation is done within disambigIobj. If no topics | |
| // match the player's input, that method will return the special catchall | |
| // topic catchallNonTopic. Since no NPC's or infoSources know about this | |
| // topic, it will simply cause a "disavow" to be printed. Similarly, if | |
| // topics match but no *known* topics match, catchallUnknownTopic will be | |
| // returned, which will cause a reply of "you don't know about that" to | |
| // any type of query (ask, tell, consult, look up). | |
| // | |
| // Important properties: none | |
| class ioTopicVerb: deepverb | |
| validIoList(actor, prep, dobj) = (nil) | |
| validIo(actor, obj, seqno) = true | |
| ioDefault(actor, prep) = (nil) | |
| disambigIobj(actor, prep, dobj, verprop, wordlist, objlist, flaglist, | |
| numberWanted, isAmbiguous, silent) = { | |
| local i, len; | |
| local newlist = []; | |
| local unknownTopicsFound = nil; | |
| len = length(objlist); | |
| for (i=1; i <= len; i++) { | |
| if (isclass(objlist[i], topic)) { | |
| if (objlist[i].known) | |
| newlist += objlist[i]; | |
| else | |
| unknownTopicsFound = true; | |
| } | |
| } | |
| if (length(newlist) < 1) { | |
| if (unknownTopicsFound) | |
| newlist += catchallUnknownTopic; | |
| else | |
| newlist += catchallNonTopic; | |
| } | |
| return newlist; | |
| } | |
| ; | |
| // doTopicVerb: a verb which uses topics as direct objects. disambigDobj | |
| // simply calls disambigIobj on ioTopicVerb. See ioTopicVerb for more | |
| // details. | |
| // | |
| // Important properties: none | |
| class doTopicVerb: deepverb | |
| validDoList(actor, prep, io) = (nil) | |
| validDo(actor, obj, seqno) = true | |
| doDefault(actor, prep, io) = (nil) | |
| // Allowing multiple objects can cause erroneous sdescs | |
| // (e.g. catchallNonTopic.sdesc) to get printed. | |
| rejectMultiDobj(prep) = { | |
| "You can't use multiple objects with that verb."; | |
| return true; | |
| } | |
| disambigDobj(actor, prep, io, verprop, wordlist, objlist, flaglist, | |
| numberWanted, isAmbiguous, silent) = { | |
| return ioTopicVerb.disambigIobj(actor, prep, io, verprop, wordlist, | |
| objlist, flaglist, numberWanted, isAmbiguous, silent); | |
| } | |
| ; | |
| /************************** adv.t Modifications **************************/ | |
| // Modifications to thing for default responses to ask, tell, consult, | |
| // look up, and redirections of ioConsultOn to doConsultOn, ioAskFor to | |
| // doAskFor. | |
| // | |
| // Important properties: none, unless you want to change default responses. | |
| modify thing | |
| replace verDoAskAbout(actor, io) = {"There is no response.";} | |
| verIoAskFor(actor) = {} | |
| verDoAskFor(actor, io) = {"There is no response.";} | |
| ioAskFor(actor, dobj) = {dobj.doAskFor(actor, self);} | |
| replace verDoTellAbout(actor, io)= {"There is no response.";} | |
| verIoConsultOn(actor) = {} | |
| verDoConsultOn(actor, io) = {"That's not an information source.";} | |
| ioConsultOn(actor, dobj) = {dobj.doConsultOn(actor, self);} | |
| verIoLookupIn(actor) = {"That's not an information source.";} | |
| verDoLookupIn(actor, io) = {} | |
| ; | |
| // Modifications to movableActor (parent class for all actors) | |
| // | |
| // Important properties: | |
| // + askTopics(topic, words): This method should output text and return | |
| // true for a valid topic, otherwise return nil, in which case disavow | |
| // will be printed by doAskAbout. The specific vocabulary words used to | |
| // refer to the topic are passed in the "words" parameter. | |
| // + tellTopics(topic, words): works almost identically to askTopics. | |
| // + doAskFor(actor, io): You should override this method if you want to | |
| // allow the player to ask this NPC *for* something. Like "ask about", | |
| // it takes topics only. By default, it simply outputs self.disavow. | |
| // + disavow: default changed to "There is no response.". This method will | |
| // be called if askTopics returns nil. | |
| // + tellDisavow: calls disavow by default. This method will be called if | |
| // tellTopics returns nil. | |
| modify movableActor | |
| askTopics(topic, words) = {return nil;} | |
| tellTopics(topic, words) = {return nil;} | |
| doAskFor(actor, io) = {self.disavow;} | |
| replace disavow = "There is no response." | |
| tellDisavow = {self.disavow;} | |
| replace doAskAbout(actor, io) = { | |
| if (!self.askTopics(io, objwords(2))) | |
| self.disavow; | |
| } | |
| verDoTellAbout(actor, io) = {} | |
| doTellAbout(actor, io) = { | |
| if (!self.tellTopics(io, objwords(2))) | |
| self.tellDisavow; | |
| } | |
| verDoAskFor(actor, io) = {} | |
| ; | |
| /**************************** Special Objects ****************************/ | |
| // catchallNonTopic: If the game is expecting a topic, and the player | |
| // enters vocabulary that does not match any objects of the topic class | |
| // (known or unknown), then the disambiguation function on ioTopicVerb or | |
| // doTopicVerb will return a single-item list consisting of | |
| // catchallNonTopic. Since no NPC or infoSource will have a response/entry | |
| // for this topic, it will simply cause a "disavow" statement to be | |
| // printed. | |
| // | |
| // Important properties: none | |
| catchallNonTopic: knownTopic | |
| sdesc = "non-topic (you should never see this)" | |
| ; | |
| // catchallUnknownTopic: Similar to catchallNonTopic, this topic will be | |
| // returned by disambiguation functions if the player's vocabulary matches | |
| // no *known* topics, but at least one unknowntopic. Default messages are | |
| // set here so that ask, tell, and consult will all output topic.unknownMsg | |
| // in response. The default message for "look up" must be handled in | |
| // infoSource itself, in ioLookupIn. | |
| // | |
| // Important properties: none | |
| catchallUnknownTopic: knownTopic | |
| sdesc = "unknown topic (you should never see this)" | |
| ioAskAbout(actor, dobj) = {topic.unknownMsg;} | |
| ioAskFor(actor, dobj) = {topic.unknownMsg;} | |
| ioTellAbout(actor, dobj) = {topic.unknownMsg;} | |
| ioConsultOn(actor, dobj) = {topic.unknownMsg;} | |
| ; | |
| /*************************** New Prepositions ***************************/ | |
| forPrep: Prep | |
| preposition = 'for' | |
| sdesc = "for" | |
| ; | |
| /************************ New and Replaced Verbs ************************/ | |
| replace askVerb: ioTopicVerb, darkVerb | |
| verb = 'ask' | |
| sdesc = "ask" | |
| prepDefault = aboutPrep | |
| ioAction(aboutPrep) = 'AskAbout' | |
| ioAction(forPrep) = 'AskFor' | |
| ; | |
| replace tellVerb: ioTopicVerb, darkVerb | |
| verb = 'tell' | |
| sdesc = "tell" | |
| prepDefault = aboutPrep | |
| ioAction(aboutPrep) = 'TellAbout' | |
| ; | |
| consultVerb: ioTopicVerb | |
| verb = 'consult' | |
| sdesc = "consult" | |
| prepDefault = onPrep | |
| ioAction(onPrep) = 'ConsultOn' | |
| ioAction(aboutPrep) = 'ConsultOn' | |
| ; | |
| lookupVerb: doTopicVerb | |
| verb = 'look up' 'read about' | |
| sdesc = "look up" | |
| prepDefault = inPrep | |
| ioAction(inPrep) = 'LookupIn' | |
| ; | |
| #pragma C- | |
Xet Storage Details
- Size:
- 12.9 kB
- Xet hash:
- e3a02738ddd608a0545bec05f5bbd82edbf36ffa0b8e5fa3fbe58d742e2dc87b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.