In each section, one or more objects are described. Consider how they might be implemented, then click on the question to check the answer.
1. A window that is painted shut. The player shouldn't find out until s/he tries opening it.
window: Fixture 'window' 'window' dobjFor(Open) { /* * It's perfectly logical to try to open the window. * So verify() allows the action. */ verify() { } /* But as the player tries to open it, we fail. */ action() { "As you try to open the window, you find that it's painted shut. "; } } ;
An alternative would be to use check() instead of action(), and then finish with exit;. You can read more about the difference in this technical article.
2. A cooking pot that's too hot to be picked up without gloves.
gloves: Wearable 'glove*gloves' 'gloves' isPlural = true ; cookingPot: Container 'hot cooking pot' 'cooking pot' "It looks hot." dobjFor(Take) { action() { /* * Check if gActor (= the actor doing the take) is * wearing the gloves. */ if (!gloves.isWornBy(gActor)) { /* * Instead of just displaying the message (using * double-quotes), we use the special macro * reportFailure. * The only difference is the result of an implied * take: * > PUT POT IN DISHWASHER * (first trying to take the cooking pot) * Ow! It's burning hot. */ reportFailure('Ow! It\'s burning hot! '); } else inherited(); } } ;
spoon: Thing 'spoon' 'spoon'; /* * We don't use the Food class, since it provides * no functionality that we want. */ soup: Thing 'hot soup' 'soup' @cookingPot "Mmm. Looks good. " /* Prevent "pot (which contains a soup)"-type messages. */ isMassNoun = true /* * We use preconditions to make sure the player is holding * the spoon. We want the objHeld precondition, but it's * not the soup that should be held. Instead, we create * an ObjectPreCondition which force the spoon to be held. * * We only want to create it once, when the code is compiled, * which is why we add the "static" keyword. */ spoonHeldPreCond = static new ObjectPreCondition(spoon, objHeld) /* Taste is the same as Eat. */ dobjFor(Taste) asDobjFor(Eat) dobjFor(Eat) { preCond = [spoonHeldPreCond] verify() { } action() { "Mmm. Tasty. "; } } dobjFor(Take) { verify() { illogical('You can\'t take that. '); } } ;
/* For a light-source, the library has a Matchstick class. */ Matchstick 'matchstick/match/stick*matchsticks sticks' 'matchstick'; dynamite: Thing 'dynamite' 'dynamite' isMassNoun = true /* Remap Burn and Extinguish to the fuse. */ dobjFor(Burn) remapTo(Burn, dynamiteFuse) dobjFor(Extinguish) remapTo(Extinguish, dynamiteFuse) ; /* * For the fuse, we use the library's Candle class. Then we * simply need to change the messages. * * Notice that 'dynamite' is in a parenthesis. * This means that 'dynamite' alone won't refer to the fuse. */ + dynamiteFuse: Candle, Component '(dynamite) fuse' 'dynamite fuse' desc() { if (isLit) "It's lit! Run! "; else "It's not lit. "; } fuelLevel = 4 sayBurnedOut() { /* * We display this without checking the player's location, * since Candle makes sayBurnedOut run in a SenseDaemon * that hides all printed messages, if the player can't see * the dynamite (well, the fuse, to be exact.) */ "\bThe dynamite fuse burns out.\b"; if (gPlayerChar.roomLocation == dynamite.roomLocation) { /* * Check if our topmost location is the same as the * player's. If it is, we print a message and end the * game, though we allow the player to undo. */ "\b<B>*** <BIG>BOOM</BIG> ***</B>\b"; finishGame([finishOptionUndo]); /* (finishGame never returns.) */ } /* Else, make the dynamite disappear from the game world. */ dynamite.moveInto(nil); } ;
DefaultAskTellTopic topicResponse() { /* Find the best match for the specified topic. */ local topic = gTopic.getBestMatch(); if (topic && topic.ofKind(Thing)) { /* There's a match, and it's a Thing. */ "<q>What can you tell me about << topic.theName >>?</q> you ask. <q>I'm afraid I don't know much about << topic.itObj >>,</q> he replies. "; } else { /* * No match, or the match is a Topic. * Since topics don't have names, we must treat * them just like we treat unrecognizable sentences. */ "You ask him about << gTopic.getTopicText() >>, but he just shrugs. "; } } ;
whiteHorse: Actor 'white black beauty/horse' 'Black Beauty' /* Avoid "You see the Black Beauty"-type messages. */ isProperName = true matchNameCommon(origTokens, adjustedTokens) { /* * If both the words 'black' and 'horse' were used, * we don't match. Nor if 'white' but not 'horse' * were used. */ if (adjustedTokens.indexOf('black') && adjustedTokens.indexOf('horse')) return nil; if (adjustedTokens.indexOf('white') && !adjustedTokens.indexOf('horse')) return nil; /* Otherwise, run the default checks. */ return inherited(origTokens, adjustedTokens); } ; blackHorse: Actor 'white black beauty/horse' 'White Beauty' isProperName = true matchNameCommon(origTokens, adjustedTokens) { if (adjustedTokens.indexOf('white') && adjustedTokens.indexOf('horse')) return nil; if (adjustedTokens.indexOf('black') && !adjustedTokens.indexOf('horse')) return nil; return inherited(origTokens, adjustedTokens); } ;
/* Define an action that takes two objects. */ DefineTIAction(CombWith); VerbRule(CombWith) ('comb' | 'groom') singleDobj 'with' singleIobj : CombWithAction verbPhrase = 'comb/combing (what) (with what)' ; /* * Assuming that leaving out the indirect object never makes sense, * we can use the following code to ask the player for one. * Note that it's the same action, just another verb rule. */ VerbRule(CombWithWhat) ('comb' | 'groom') singleDobj : CombWithAction verbPhrase = 'comb/combing (what) (with what)' construct() { /* These two lines makes the parser ask for an object. */ iobjMatch = new EmptyNounPhraseProd(); iobjMatch.responseProd = withSingleNoun; } ; /* * Provide a default response to CombWith. * This is optional; without it, TADS will just reply * "You can't do that.". */ modify Thing dobjFor(CombWith) /* We're being combed */ { preCond = [touchObj] verify() { illogical('{You/he} can\'t comb {the dobj/him}. '); } } iobjFor(CombWith) /* We're being used to comb something else */ { preCond = [objHeld] verify() { illogical('{You/he} can\'t comb anything with {the iobj/him}. '); } } ; me: Person; + comb: Thing 'comb' 'comb' iobjFor(CombWith) { verify() { } } ; + myHair: Fixture 'hair' 'hair' theName = 'your hair' isMassNoun = true dobjFor(CombWith) { verify() { } action() { "Now it's nice. "; } } ;
/* Change me to this: */
me: Person
checkTouchViaPath(obj, dest, op)
{
if (dest == myHair && hat.isWornBy(self))
return new CheckStatusFailure(
'Your hat is in the way. ', dest);
return checkStatusSuccess;
}
;
+ hat: Wearable 'hat' 'hat'
wornBy = me
;
This example could easily be extended to make the player
automatically remove the hat, using preconditions.
Have a look at the OutOfReach class in objects.t to see how.
9. Handle SHOOT GUN, SHOOT MAN, SHOOT AT MAN and SHOOT AT MAN WITH GUN.
DefineTAction(Shoot); /* Ambiguous - shooting at or with? */ DefineTAction(ShootAt); /* Missing weapon. */ DefineTAction(ShootWith); /* Missing target. */ DefineTIAction(ShootAtWith); /* Complete command. */ /* SHOOT GUN or SHOOT MAN. */ VerbRule(Shoot) 'shoot' singleDobj : ShootAction verbPhrase = 'shoot/shooting (what)' ; /* SHOOT AT MAN. */ VerbRule(ShootAt) ('shoot' | 'fire') 'at' singleDobj : ShootAtAction verbPhrase = 'shoot/shooting (at what)' ; /* SHOOT WITH GUN. */ VerbRule(ShootWith) (('shoot' | 'fire') 'with' | 'fire') singleDobj : ShootWithAction verbPhrase = 'fire/firing (what)' ; /* SHOOT MAN (dobj) WITH GUN (iobj). */ VerbRule(ShootAtWith) ('shoot' | 'fire') ('at' | ) singleDobj 'with' singleIobj : ShootAtWithAction verbPhrase = 'shoot/shooting (at what) (with what)' ; /* SHOOT GUN (iobj) AT MAN (dobj). */ VerbRule(ShootWithAt) ('shoot' | 'fire') ('with' | ) singleIobj 'at' singleDobj : ShootAtWithAction verbPhrase = 'shoot/shooting (at what) (with what)' ; modify Thing /* For most Things, Shoot means Shoot At. */ dobjFor(Shoot) asDobjFor(ShootAt) dobjFor(ShootAt) { verify() { illogical('{You/he} can\'t shoot at {the dobj/him}. '); } } dobjFor(ShootWith) { preCond = [objHeld] verify() { illogical('{You/he} can\'t shoot with {the dobj/him}. '); } } dobjFor(ShootAtWith) { verify() { illogical('{You/he} can\'t shoot at {the dobj/him}. '); } } iobjFor(ShootAtWith) { preCond = [objHeld] verify() { illogical('{You/he} can\'t shoot with {the iobj/him}. '); } } ; gun: Thing 'gun' 'gun' @me /* For a gun, Shoot means Shoot With. */ dobjFor(Shoot) asDobjFor(ShootWith) dobjFor(ShootWith) { verify() { } /* Find a suitable dobj (target), making me iobj. */ action() { askForDobj(ShootAtWith); } } iobjFor(ShootAtWith) { verify() { } /* We let the dobj (target) handle the shooting. */ } ; me: Person dobjFor(ShootAt) { verify() { } /* Find a suitable iobj (weapon). */ action() { askForIobj(ShootAtWith); } } dobjFor(ShootAtWith) { verify() { } action() { "\b<B>*** You have died! ***</B>\b"; finishGame([finishOptionUndo]); } } ;
psychiatrist: Person 'bearded doctor/psychiatrist/psychologist/shrink' 'bearded psychiatrist' ; + HermitActorState /* This is the psychiatrist's initial state. */ isInitState = true /* Shown in room descriptions when we're present. */ specialDesc = "A bearded psychiatrist has you under observation. " /* * So, what's with the extraReports, you wonder? * Well, if the text was simply displayed using * regular double-quotes, it'd cause default reports * (like "Taken." and "Dropped.") to be suppressed: * * >take thing * "Subject feels lack of the thing. (...)" * * > * * By explicitly making the texts extraReports, they don't * suppress the default report. */ beforeAction() { if (gActionIs(Take)) extraReport('<q>Subject feels lack of {the dobj/him}. Suppressed Oedipal complex? Hmm.</q>\b'); } afterAction() { if (gActionIs(PutIn)) extraReport('\b<q>Subject associates {the dobj/him} with {the iobj/him}. Interesting.</q>\b'); else if (gActionIs(PutOn)) extraReport('\b<q>Subject puts {the dobj/him} on {the iobj/him}. Interesting.</q>\b'); else if (gActionIs(Look)) extraReport('\b<q>Pretend I'm not here.</q>\b'); } /* * While beforeAction, afterAction, specialDesc and isInitState * are available for any ActorState, noResponse is special to * HermitActorState. */ noResponse() { "He is fascinated by your behaviour, but makes no attempt to interfere with it. "; } ;
Inform authors might want to compare this with the Inform-version.
Copyright 2002-13 Søren Løvborg. The text of this page is licensed under a Creative Commons Attribution-Share Alike 3.0 License.