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.