! -------------------------------------------------------------------------
! verbs.inf -- A component of the Inform 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, ")^";
    <<PourInto second noun>>;
  }
  else if (second == player) {
    print "(entering ", (the) noun, ")^";
    <<Enter noun>>;
  }
  else if (noun == player) {
    print "(eating ", (the) second, ")^";
    <<Eat second>>;
  }
  else if (noun has supporter) {
    print "(putting ", (the) noun, " on ", (the) second, ")^";
    <<PutOn second noun>>;
  }
  else if (noun has container) {
    print "(putting ", (the) noun, " in ", (the) second, ")^";
    <<Insert second noun>>;
  }
  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) <<Enter noun>>;
  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) <<Enter noun>>;
  if (noun == player) "An amazingly acrobatic feat. Well, it would have been if
    you did it, which you didn't.";
  <<Attack noun>>;
];

! (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 <money> to NPC.'
    else {
      print "(giving ", (the) money_obj, " to ", (the) noun, ")^";
      <<Give money_obj 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.
      <<Pay obj>>;
  "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;
  <<Pay noun>>;
];

! (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, ")^";
    <<Give noun 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, ")^";
        <<PourOnto x 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) <<PourInto noun second>>;
  ! 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, ")^";
    <<Drink 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.";
  <<TieTo rope noun>>;
];

! (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, ")^"; <<Untie 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, ")^";
    <<SwitchOn noun>>;
  }
  else {
    print "off ", (the) noun, ")^";
    <<SwitchOff 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;
  <<Enter noun>>;
];

! (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>
!   TAKE THING FROM SOMEWHERE*  <Remove thing>
!   MOVE THING TO SOMEWHERE*    <Transfer thing table>
!
! * 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;
  <<Take noun>>;
];

! (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) <<Insert noun second>>;
  if (second has supporter) <<PutOn noun second>>;
  <<Drop noun>>;
];