! ------------------------------------------------------------------------- ! $Id: verbs.inf,v 1.1 2000/08/20 17:29:18 neilc Exp $ ! verbs.inf -- A component of the Inform 6 port of Ditch Day Drifter ! ! This file contains grammar declarations, action routines and token ! parsing routines. ! ------------------------------------------------------------------------- ! ------------------------------------------------------------------------- ! -- Table of Contents -- ! ! SPECIAL PARSING ROUTINES (9) ! noun=Routine tokens (9.1) ! AssumeKey (9.1.1) ! AssumeKeyboard (9.1.2) ! AssumeNotAnimate (9.1.3) ! AssumeRailing (9.1.4) ! AssumeRope (9.1.5) ! ! knock_knock_joke global (P) ! ! General Parsing Routines (9.2) ! TopicOn (9.2.1) ! topic object (9.2.2) ! MatchMultiWordNothingHeld (9.2.3) ! ParseAnything (9.2.4) ! ParseThere (9.2.5) ! ParseWho (9.2.6) ! ! GRAMMAR (10) ! climb up/down (10.1) ! ClimbUpSub (10.1.1) ! ClimbDownSub (10.1.2) ! drink (10.2) ! DrinkSub (10.2.1) ! drop (10.3) ! DropAllErrorSub (10.3.1) ! eye (10.4) ! fill (10.5) ! Liquid (10.5.1) ! FillSub (10.5.2) ! hello (10.6) ! HelloSub (10.6.1) ! jump into/on/off (10.7) ! JumpIntoSub (10.7.1) ! JumpOnSub (10.7.2) ! kick down/over/open (10.8) ! knock (10.9) ! 'knock-knock' (10.9.1) ! KnockSub (10.9.2) ! KnockInsultSub (10.9.3) ! KnockOnSub (10.9.4) ! "who's" (10.9.5) ! 'who' (10.9.6) ! WhosThereSub (10.9.7) ! 'madame' (10.9.8) ! NonVerbSub (10.9.9) ! MadameWhoSub (10.9.10) ! lock and unlock (10.10) ! look out (10.11) ! make (10.12) ! MakeSub (10.12.1) ! paying for things (10.13) ! 'pay' (10.13.1) ! 'bribe' (10.13.2) ! 'purchase' (10.13.3) ! PayInanimateSub (10.13.4) ! PaySub (10.13.5) ! PayForSub (10.13.6) ! PayCreatureForThingSub (10.13.7) ! PayThingForThingSub (10.13.8) ! pouring liquids (10.14) ! PourOntoSub (10.14.1) ! PourIntoSub (10.14.2) ! throw (10.15) ! tie and untie (10.16) ! 'tie' (10.16.1) ! TieRopeSub (10.16.2) ! TieToSub (10.16.3) ! 'untie' (10.16.4) ! UntieSub (10.16.5) ! type (10.17) ! TypeOnSub (10.17.1) ! sleep (10.18) ! SleepSub (10.18.1) ! switch (10.19) ! SwitchVagueSub (10.19.1) ! under (10.20) ! 'put under' (10.20.1) ! 'hide' 'stash' (10.20.2) ! 'hide in' (10.20.3) ! HideMeSub (10.20.4) ! HideInSub (10.20.5) ! PutUnderSub (10.20.6) ! 'get under' (10.20.7) ! 'go under' (10.20.8) ! GoUnderSub (10.20.9) ! LookUnderSub (10.20.10) ! xyzzy (10.21) ! MagicWordSub (10.21.1) ! hollow_voice (10.21.2) ! ! ACTION REPLACEMENTS (11) ! EatSub (11.1) ! RemoveSub (11.2) ! TransferSub (11.3) ! ! ------------------------------------------------------------------------- ! (9) SPECIAL PARSING ROUTINES ! (9.1) noun=Routine tokens ! (9.1.1) [ AssumeKey; if (noun == master_key) rtrue; else rfalse; ]; ! (9.1.2) [ AssumeKeyboard; if (noun == computer or control_pad) rtrue; else rfalse; ]; ! (9.1.3) [ AssumeNotAnimate; if (noun hasnt animate) rtrue; else rfalse; ]; ! (9.1.4) [ AssumeRailing; if (noun == railing) rtrue; else rfalse; ]; ! (9.1.5) [ AssumeRope; if (noun == rope) rtrue; else rfalse; ]; ! (P) knock_knock_joke global ! I'm going to use this global, in conjunction with some general parsing ! routines, to prevent the library from recognizing the knock-knock joke ! grammar unless the joke is being told. Global knock_knock_joke=0; ! 0 -- No knock-knock joke. ! 1 -- Listening for "who's there?" ! 2 -- Listening for "madame who?" ! (9.2) General Parsing Routines ! (9.2.1) ! This routine is a general parsing token that gobbles up anything the ! player types, up to the word 'on'. If it gobbled past 'on', the parser ! would end up reading right over the part of the sentence that indicated ! what you wish to type on. ! ! This routine also sets 'noun' to equal the dummy object, "topic". This ! made it easier to write the TypeOn action, since noun and second are ! guaranteed to hold some value. The most useful one being second, which ! holds the value of the item typed on. [ TopicOn i; do { i=NextWordStopped(); if (i == 'on') { wn--; break; } } until (i == -1); return topic; ]; ! (9.2.2) topic object Object topic "that" has proper; ! (9.2.3) [ MatchMultiWordIfNothingHeld; if (children(player) == 0 && NextWord() == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) return 0; else return -1; ]; ! (9.2.4) [ ParseAnything wd; do wd=NextWordStopped(); until (wd == -1); return 0; ]; ! (9.2.5) [ ParseThere wd; if (knock_knock_joke ~= 1) return -1; wd=NextWord(); if (wd == 'there' or 'there?') return 0; else return -1; ]; ! (9.2.6) [ ParseWho wd; if (knock_knock_joke ~= 2) return -1; wd=NextWord(); if (wd == 'who' or 'who?') return 0; else return -1; ]; ! (10) GRAMMAR ! (10.1) climb up/down ! The rope has to know if it is being climbed "up" or climbed "down". Extend 'climb' replace * noun -> Climb * 'up' noun -> ClimbUp * 'down' noun -> ClimbDown * 'down' 'from' -> GetOff * 'under' noun -> GoUnder * 'onto'/'on' noun -> Enter * 'on' 'top' 'of' noun -> Enter * 'into' noun -> Enter; ! (10.1.1) [ ClimbUpSub; ! Climbing up or down yourself is an acknowledgments easter egg. if (noun == player) return EasterEgg(); "You can't climb up ", (thatOrThose) noun, "."; ]; ! (10.1.2) [ ClimbDownSub; if (noun == player) return EasterEgg(); "You can't climb down ", (thatOrThose) noun, "."; ]; ! (10.2) drink Verb 'imbibe' 'slurp' 'guzzle' = 'drink'; ! (10.2.1) [ DrinkSub par_obj; if (noun == player) "That would be rather difficult."; else if ((~~ noun ofclass LiquidNitrogen) && noun ~= toxicola) "You can't drink ", (thatOrThose) noun, "."; else if (noun hasnt edible) "", (The) noun, " doesn't appear appetizing."; else { par_obj=parent(noun); if (par_obj ~= 0 && par_obj has container && IsNotHeldOrTaken(par_obj)) return; remove noun; "Delicious!"; } ]; ! (10.3) 'drop' ! (10.3.1) ! Trap "drop all" attempts when the player is empty handed (general parsing ! routine). The ALLn__WD constants used are defined in "english.h". ! ! Otherwise, "drop all" would match the grammar line that became an ! ##Insert action, and "drop all" would generate the confusing question: ! "What do you want to drop those things into?". With the ! MatchMultiWordIfNothingHeld token, "drop all" when empty handed results ! in the better response, "There are none at all available." ! This is the same grammar line as appears in grammar.h, but with the last ! line, which resulted in ##ThrowAt removed, and the ! MatchMultiWordIfNothingHeld line added. Extend 'drop' first * MatchMultiWordIfNothingHeld -> DropAllError * multiheld -> Drop * multiheld 'in'/'into'/'down' noun -> Insert * multiheld 'on'/'onto' noun -> PutOn; ! (10.3.2) [ DropAllErrorSub; return L__M(##Miscellany, 44); ! There are none at all available. ]; ! (10.4) eye ! The guard 'eyes you warily', so it stands to reason a player might want to do ! it right back. Verb 'eye'='examine'; Extend only 'eye' * noun 'warily' -> Examine; ! (10.5) fill ! (10.5.1) ! This routine will select liquids by default for the fill action. ! ! Note: Due to a slightly arcane restriction of Inform, a routine used as a ! grammar token must be defined before the verb directive it appears in. [ Liquid; if (noun ofclass LiquidNitrogen || noun == toxicola) rtrue; else rfalse; ]; ! You can use a noun=Routine token to force the library to assume indirect ! objects. There are two entries for the Fill action because I want liquid ! to be assumed, but if that fails, players can still pour anything they ! want to. ! ! The order of the grammar lines in a Verb directive is important. ! The parser tries to match them one by one, starting at the topmost, and ! gives up as soon as it finds a valid match. In the following declaration, ! liquids will be considered before non-liquids, because the grammar line ! including the noun=Liquid token will be checked before the next grammar ! line, which allows any indirect object. Extend 'fill' replace * noun 'with' noun=Liquid -> Fill * noun 'with' noun -> Fill; ! (10.5.2) ! It is helpful to think of this improved ##Fill action as a switching ! station at a railroad yard. It looks at the incoming cargo (noun and ! second), and sends them on their way to the most appropriate destination ! (a new action). Switching station actions like this can be confusing to ! the player, so print a braced message telling the player what's ! happening. [ FillSub; if (second ofclass LiquidNitrogen || second == toxicola) { print "(pouring ", (the) second, " into ", (the) noun, ")^"; <>; } else if (second == player) { print "(entering ", (the) noun, ")^"; <>; } else if (noun == player) { print "(eating ", (the) second, ")^"; <>; } else if (noun has supporter) { print "(putting ", (the) noun, " on ", (the) second, ")^"; <>; } else if (noun has container) { print "(putting ", (the) noun, " in ", (the) second, ")^"; <>; } else print_ret (The) noun, " can't be filled."; ]; ! (10.6) hello Verb 'hello' 'hi' 'greetings' 'salutations' * -> Hello; ! (10.6.1) [ HelloSub; ! Cribbed from the original Ditch Day Drifter. "Nice weather we've been having."; ]; ! (10.7) jump into/on/off Extend 'jump' * 'into' noun -> JumpInto * 'on' noun -> JumpOn * 'off' noun -> GetOff; ! (10.7.1) [ JumpIntoSub; if (noun has enterable) <>; if (noun == player) "You're already much too ~into~ yourself as it is."; if (noun has animate) "It doesn't look like ", (the) noun, " is really into slam dancing."; "That would hurt you more than ", (the) noun, "."; ]; ! (10.7.2) [ JumpOnSub; if (noun has enterable) <>; if (noun == player) "An amazingly acrobatic feat. Well, it would have been if you did it, which you didn't."; <>; ]; ! (10.8) kick down/over/open ! For all you kickers out there, and I think I know who you are. Verb 'kick' * 'down'/'over'/'open' noun -> Attack * noun 'down'/'over'/'open' -> Attack; ! (10.9) knock Verb 'knock' * -> Knock * 'knock' -> Knock * creature -> KnockInsult * 'on'/'at' noun -> KnockOn * 'over'/'down' noun -> Attack * noun 'over'/'down' -> Attack; ! (10.9.1) Verb 'knock-knock' * -> Knock; ! (10.9.2) [ KnockSub; "There's no answer."; ]; ! (10.9.3) [ KnockInsultSub; if (noun == player) "Don't be so hard on yourself. You won't be a freshman forever."; "You just can't think of a suitably scathing insult."; ]; ! (10.9.4) [ KnockOnSub; if (noun has door && noun has openable) { if (noun hasnt open) "Nobody opens the door."; ! I will waste a pile of code on a useless knock-knock joke easter egg, ! which you get by knocking on open doors. else { knock_knock_joke=1; ! Initiate the joke. "Knock, knock."; } } if (noun == player) "Ow."; if (noun has animate) "I don't think ", (the) noun, " would care for that."; "", (The) noun, " ", (IsOrAre) noun, " unresponsive."; ]; ! (10.9.5) Verb 'who^s' * -> NonVerb * ParseThere -> WhosThere * ParseAnything -> NonVerb; ! (10.9.6) Verb 'who' * -> NonVerb * 'is' -> NonVerb * 'is' ParseThere ->WhosThere * ParseAnything -> NonVerb; ! (10.9.7) [ WhosThereSub; knock_knock_joke=2; ! Recognize "madame who?" "Madame."; ]; ! (10.9.8) Verb 'Madame' * -> NonVerb * ParseWho -> MadameWho * ParseAnything -> NonVerb; ! (10.9.9) ! Simulate the libraries standard response to unknown verbs. [ NonVerbSub; "That's not a verb I recognize."; ]; ! (10.9.10) [ MadameWhoSub; knock_knock_joke=0; ! Turn off the knock-knock joke grammar. "Madame knock-knock joke isn't very funny."; ]; ! (10.10) lock and unlock ! The key will only be assumed for lockable objects. Extend 'lock' first * lockable 'with' noun=AssumeKey -> Lock; Extend 'unlock' first * lockable 'with' noun=AssumeKey -> Unlock; ! (10.11) look out ! So you can look out the windows. Extend 'look' * 'out' noun -> Search; ! (10.12) make Verb 'make' * noun -> Make; ! (10.12.1) [ MakeSub; "The only thing at CalTech that needs making is beds."; ]; ! (10.13) paying for things ! (10.13.1) Extend only 'pay' replace ! It turns out that this is the best place to check for this error ! condition. This traps nonsense phrases like, "pay desk". * noun=AssumeNotAnimate -> PayInanimate * 'for' noun -> PayFor * 'for' noun 'with' held -> PayThingForThing reverse * creature -> Pay * creature 'for' noun -> PayCreatureForThing * creature 'with' held -> Give reverse * held 'for' noun -> PayThingForThing; ! (10.13.2) Verb 'bribe' * noun=AssumeNotAnimate -> PayInanimate * creature -> Pay * creature 'with' held -> Give reverse; ! (10.13.3) Extend 'purchase' replace * noun -> PayFor * noun 'from' creature -> Pay reverse * noun 'with' held -> PayThingForThing reverse; ! (10.13.4) [ PayInanimateSub; "You can only do that to something animate."; ]; ! (10.13.5) ! ------------------------------------------------------------------------- ! Paying for an item, no matter how you word it, is eventually converted to ! a simple Give action. The only twist is that if the player indicated what ! they are paying for, that item will be entered into the 'buy_obj' global ! variable. ! ! I haven't implemented a 'third' here. You can't type, for instance, "pay ! the clerk for the battery with the one-dollar bill." It could be done ! though. See Lucian P. Smith's code for the hunk of bark in his released ! subset of his code for the game "The Edifice". ! ------------------------------------------------------------------------- [ PaySub money_obj; ! There are only two money objects in the game and the player cannot ! possess them both at the same time. It is safe to assume one or the ! other if the player did not specify. If they did specify a specific ! object with which to pay, it is handled by the PayThingForThing action ! (below). if (IndirectlyContains(player,one_dollar)) money_obj=one_dollar; if (IndirectlyContains(player,five_dollars)) money_obj=five_dollars; if (money_obj ~= 0) { if (noun == player) "You hand ", (the) money_obj, " to yourself. Due to this shrewd investment, your funds are now 100% of their former value."; ! So 'pay NPC' is interpreted as 'give to NPC.' else { print "(giving ", (the) money_obj, " to ", (the) noun, ")^"; <>; } } else "You don't have any money."; ]; ! (10.13.6) [ PayForSub obj; ! The player expressed a desire for a certain object. Save that in the ! buy_obj global variable. Before routines for animate objects have to ! look at that to see if it is important. See the code for the clerk and ! Lloyd. The AfterPrompt routine ["entrypts.inf":8.1] sets buy_obj back ! to zero (nothing) at the end of every turn. buy_obj=noun; if (noun == player) "You're priceless."; ! The player indicated what they wanted to buy, but not who they are ! paying. This routine will try to guess who the player wanted to pay. It ! gives up at the first animate object (besides the player) it encounters ! in the current location. objectloop(obj in location) if (obj has animate && obj ~= player) ! It is re-routed to the Pay action above. <>; "I can't see who you are trying to pay."; ]; ! (10.13.7) [ PayCreatureForThingSub; ! The player indicated what they want to buy, and who they are paying. ! Save the purchase item in buy_obj, and then re-route to the pay ! action. buy_obj=second; <>; ]; ! (10.13.8) [ PayThingForThingSub obj buyer; ! The player indicated what they are buying and what they are using for ! money. ! Assume a buyer, same as above. objectloop (obj in location) if (obj has animate && obj ~= player) buyer=obj; if (buyer ~= 0) { buy_obj=second; print "(giving ", (the) noun, " to ", (the) buyer, ")^"; <>; } "I can't see whom you are trying to pay."; ]; ! (10.14) pouring liquids Verb 'pour' 'dump' 'spill' * noun 'onto'/'on' noun -> PourOnto * noun 'into'/'in'/'down'/'through' noun -> PourInto; ! (10.14.1) ! With great reluctance, pouring things onto other things has been ! implemented. To make it easier, it attempts to re-route to other actions ! if at all possible. [ PourOntoSub x; ! If the poured item isn't a liquid, see if it is a container for liquid. ! If it is, pour the liquid instead. if ((~~ noun ofclass LiquidNitrogen) && (noun ~= toxicola)) { if (noun has container) { x=child(noun); if (x && (x ofclass LiquidNitrogen || x == toxicola)) { print "(pouring ", (the) x, " onto ", (the) second, ")^"; <>; } } "You can only pour liquids."; } ! Pouring onto a container is the same as pouring into it. It is too bad ! that this doesn't make sense for all possible containers. if (second has container) <>; ! More lame excuses. So sue me. if (second == player) "This is Ditch Day, not bath day."; if (second has animate) "I don't think ", (the) second, " would care for that."; switch (noun) { bottled_ln, original_ln: print "You attempt to pour "; if (noun == bottled_ln) { print "the "; remove noun; } else print "some "; "liquid nitrogen onto ", (the) second, ", but only succeed in creating a cloud of steam that floats away."; toxicola: "Pouring ", (the) noun, " onto ", (the) second, " would make a horrible brown viscous mess."; } ]; ! (10.14.2) ! For an action that may have many error conditions, it requires a fair ! amount of soul searching to decide the order in which the error ! conditions should be tested for. In this case, the following is adopted: ! ! Is the player pouring a non-liquid? ! Is the player pouring an object back into itself? ! Is the player pouring a liquid into herself? ! Is the player pouring into something that isn't a container? ! Is the player pouring a container into itself? ! Is the container being poured out of closed? ! Is the container being poured into closed? ! ! Another ordering of the above steps might make sense, too. See the ! standard library file, verblibm.h for a good idea about how to go about ! this. [ PourIntoSub out_of_obj; ! There are only two kinds of liquid in this game. If there were much ! more than that, I would probably use a 'liquid' attribute ! instead. ! This calculation is parsed, perhaps surprisingly, into: ! ~~ (noun ofclass LiquidNitrogen || noun == toxicola) if (~~ noun ofclass LiquidNitrogen || noun == toxicola) "You can only pour liquids."; if (noun == second) "Nothing happens."; if (second == player) { print "(drinking ", (the) noun, ")^"; <>; } if (second hasnt container && second ~= funnel or empty_glass) print_ret (The) second, " is not a suitable container for liquids."; out_of_obj=parent(noun); if (second == out_of_obj) print_ret (The) noun, " is already in ", (the) out_of_obj, "."; if (out_of_obj hasnt open) "Nothing comes out, since ", (the) out_of_obj, " is closed."; if (second hasnt open && second ~= funnel or empty_glass) "You must open ", (the) second, " first."; if (IsNotHeldOrTaken(out_of_obj)) return; ! Send a message to the poured into object telling it what is up. if (second provides poured_into && second.poured_into()) return; switch (noun) { bottled_ln, original_ln: "You know from experience that ", (the) second, " is not a safe container for the liquid nitrogen."; toxicola: "Pouring ", (the) noun, " into ", (the) second, " would make a horrible brown viscous mess."; } ]; ! (10.15) throw ! I replaced the 'throw' grammar with new grammar so that "throw object" is ! no longer equivalent to "drop object". Extend only 'throw' replace * held 'at'/'against'/'on'/'onto' noun -> ThrowAt * multiexcept 'in'/'into'/'down' noun -> Insert; ! (10.16) tie and untie ! (10.16.1) ! If the player ties, the parser can assume: direct object, rope ! and indirect object, railing. You might normally use ChooseObjects for ! preferences of this sort, but you can't in cases where both a direct and ! indirect object are involved. Extend 'tie' replace * noun=AssumeRope 'to' noun=AssumeRailing -> TieTo * noun=AssumeRope 'to' noun -> TieTo * noun 'up' -> TieRope * noun 'to' noun -> TieTo * noun=AssumeRailing 'with' noun=AssumeRope -> TieTo reverse * noun 'with' noun=AssumeRope -> TieTo reverse * noun 'with' noun -> TieTo reverse * 'up' noun -> TieRope; ! (10.16.2) [ TieRopeSub; if (IsNotHeldOrTaken(rope)) "That's difficult without a rope."; <>; ]; ! (10.16.3) [ TieToSub; if (noun ~= rope && noun ~= player ) print_ret "You can't make knots with ", (a) noun, "."; ! Whenever I test new actions I invariably try doing them to myself first ! since that is the most easily available object. Hence the large number ! of time in my code that this case is checked for. if (player == noun or second) "You're not fit to be tied."; if (IsNotHeldOrTaken(noun)) return; ! ["misc.inf":12.3] ! Inform the tied_to object of the impending crisis. if (second provides tied_to) if (second.tied_to()) return; ! Players cannot go around tying up any old NPC. if (second has animate) "Leave ", (the) second, " out of your strange bondage fantasy, please."; ! The rope ["gue.inf":4.14.1] can only be tied to the railing. "You can't tie the rope to ", (thatorthose) second, "."; ]; ! (10.16.4) Verb 'untie' * noun=AssumeRope -> Untie * noun -> Untie; ! (10.16.5) [ UntieSub; if (noun == player) "Your not tied up at the moment."; if (noun == railing && rope.number == railing) { print "(untying ", (the) rope, ")^"; <>; } print_ret (The) noun, " isn't tied to anything."; ]; ! (10.17) type ! In Ditch Day, you can type on the computer or the keypad of the control ! panel. When you type anything on the computer, or something other than a ! number on the keypad, a generic error message is generated. ! ! You can type numbers or other text, but only numbers need to be ! recognized by Ditch Day. Verb 'type' ! Keyboards get preference over other objects. * number 'on' noun=AssumeKeyboard -> TypeOn * number 'on' noun -> TypeOn * TopicOn 'on' noun=AssumeKeyboard -> TypeOn * TopicOn 'on' noun -> TypeOn; ! (10.17.1) ! Objects that can be typed on may provide a 'typed_on' property that this ! routine calls. [ TypeOnSub; if (second == player) "You are not a keyboard."; if (noun ~= topic && noun > 9999) "You cannot type numbers higher than 9999."; if (second provides typed_on) if (second.typed_on()) return; "Trying to type things on ", (a) second, " isn't very productive."; ]; ! (10.18) sleep Extend 'sleep' * 'in'/'on' noun -> Sleep * 'with' noun -> Strong; ! (10.18.1) ! The TADS source code released by Mike Roberts did not include the sleep ! routine. I took a wild guess at it based on my experiments with the TADS ! game file. The sleep limit in Ditch Day is so high that I assume that ! Mike didn't want it to be a factor. [ SleepSub; if (player.awake_time < SLEEP_LIMIT) "You're not tired."; if (noun == 0 && location == RoomThree) noun=bed; if (noun ~= 0 && (noun hasnt enterable || noun has door)) "You can't sleep on ", (thatOrThose) noun, "."; ! If the player specified what they wanted to sleep in, try entering it ! before sleeping. if (noun ~= 0 && player notin noun) { print "(getting into ", (the) noun, ")^"; SilentAction(##Enter, noun); } if (player notin bed) "I don't know about you, but I can never sleep standing up. You should find a nice comfortable bed somewhere."; player.awake_time=0; "You quickly drift off into dreamland..."; ]; ! (10.19) switch ! Write new switch grammar such that it decides sensibly between ##SwitchOn ! and ##SwitchOff. Extend 'switch' replace * noun -> SwitchVague * noun 'on' -> SwitchOn * noun 'off' -> SwitchOff * 'on' noun -> SwitchOn * 'off' noun -> SwitchOff; ! (10.19.1) [ SwitchVagueSub; if (noun hasnt switchable) "That's not something you can switch."; print "(switching "; if (noun hasnt on) { print "on ", (the) noun, ")^"; <>; } else { print "off ", (the) noun, ")^"; <>; } ]; ! (10.20) under ! The dollar bill hidden under the bed introduces the concept of 'under' to ! Ditch Day. ! ! Players may try: putting the bill back under the bed, after they have ! found it; putting things under the bed; or putting things under other ! things. This implementation tries to avoid difficulties by utilizing ! snarky error messages. Exceptions are allowed for through before ! routines. See the bed ["surface.inf":1.1.4], the safe ! ["gue.inf":5.6.1], or the pit ["gue.inf":4.15.1] for examples. ! (10.20.1) Extend 'put' first * held 'under' noun -> PutUnder * 'under' noun -> GoUnder; ! (10.20.2) Verb 'hide' 'stash'='put'; ! (10.20.3) Extend only 'hide' first * -> HideMe * 'in' noun -> HideIn; ! (10.20.4) [ HideMeSub; "Hiding won't solve anything."; ]; ! (10.20.5) [ HideInSub; <>; ]; ! (10.20.6) [ PutUnderSub; ! Send a message to the offended object. The object can do something ! more interesting than this routine if it wants to. See the bed ! ["surface.inf":1.1.4] for an example. if (second provides put_under && second.put_under() == 1) return; "Putting things under ", (the) second, " isn't notably helpful."; ]; ! (10.20.7) Extend 'get' * 'under' noun -> GoUnder; ! (10.20.8) Extend 'go' * 'under' noun -> GoUnder; ! (10.20.9) [ GoUnderSub; "Such an act is beneath even you."; ]; ! (10.20.10) [ LookUnderSub; ! From Deadline. "Nope. Nothing hiding under ", (the) noun, "."; ]; ! (10.21) xyzzy Verb 'xyzzy' 'plugh' 'zork' 'wazzum' * ParseAnything -> MagicWord; ! (10.21.1) [ MagicWordSub; ! A delayed "Fool!". StartTimer(hollow_voice, random(5)+5); return NonVerbSub(); ]; ! (10.21.2) Object hollow_voice with time_left 0, time_out [; move secret_note to player; "^A hollow voice says, ~This game contains no magic-word easter-eggs.~"; ], ; Object secret_note "secret note" class Crawlable with name 'secret' 'note', description [; remove self; "The note says: ~Fool!~"; ], ; ! (11) ACTION REPLACEMENTS ! (11.1) ! The generic Eat action is replaced with one that updates the ! player's hungry_time property. [ EatSub; if (noun == player) "A hollow voice says, ~Yuck!~"; if (noun has animate) print_ret (The) noun, " wouldn't agree to be eaten."; if (noun hasnt edible) print_ret (The) noun, " doesn't appear appetizing."; if (IsNotHeldOrTaken(noun)) return; ! "misc.inf":12.3 remove noun; player.hungry_time=0; if (AfterRoutines() == 1) return; if (keep_silent == 1) return; "That was Delicious!"; ]; ! (11.2) ! In standard Inform there are three ways for an object to enter the ! player's possession: ! ! Sample command Action generated ! -------------------------- ---------------------- ! TAKE THING ! TAKE THING FROM SOMEWHERE* ! MOVE THING TO SOMEWHERE* ! ! * Only applies if the THING is inside something else. ! ! This makes it annoying to code an uncarriable object (like liquids) ! because you are forced to worry about three possible actions: Take, ! Remove and Transfer. With the following changes to the Remove and ! Transfer actions, a Take action is always generated. ! ! The Remove and Transfer actions are sort of obsolete in Inform 6 with ! library 6/10. [ RemoveSub; <>; ]; ! (11.3) [ TransferSub; ! these lines added if (noun notin player) { SilentAction(##Take, noun); if (noun notin player) return; } ! end of additions ! This line commented out: ! if (noun notin player && AttemptToTakeObject(noun)) return; if (second has container) <>; if (second has supporter) <>; <>; ];