! ----------------------------------------------------------------------------
!  The Elevator                                                      28/01/98
!
!  An elevator object, implemented with Inform 6.14 Library 6/7. Most
!  descriptions are ripped off the immortal game Lurking Horror. (I
!  sincerely hope that such quotes are covered by the fair use clause.)
!  The code is copyleft.
!
!  The elevator uses the following strategy: it moves in any given
!  direction, servicing each floor which has been called from within
!  itself or from a call button corresponding to the current direction.
!  At the end of each journey, the elevator reverses directions and
!  services the remaining calls, if any.
!
!  V2: Oops, the command "push u" (short for "push up-arrow button")
!  triggered the question "what do you mean, the ceiling or the button",
!  which of course is plainly annoying. I fixed it, sort of, via the
!  ChooseObjects routine (see p. 165 in the manual).
!
!  - Martin Braun
!  [email protected]
!
! ----------------------------------------------------------------------------

Switches scd;

Constant DEBUG;
Include "Parser";
Include "VerbLib";

! ----------------------------------------------------------------------------
!  Global variables and arrays
! ----------------------------------------------------------------------------

Global elevDest = 0;          ! current destination, 0 = no active task
Global elevMoveCounter = 4;
Global elevDirection = 0;     ! 1 when moving up, 2 when moving down

Array ElevButtons -> 9;
Array UpButtons -> 9;
Array DownButtons -> 9;

! ----------------------------------------------------------------------------
!  The ChooseObjects routine
! ----------------------------------------------------------------------------

[ ChooseObjects obj code;
   if (code < 2 || action_to_be == ##Go) rfalse;
   if (obj == UpButton or DownButton) return 3;
   rfalse;
];

! ----------------------------------------------------------------------------
!  Class definition: some locations outside the elevator
! ----------------------------------------------------------------------------

Class Stairs
with description
     [; print "This is the ";
        switch(self.number)
        {   1: print "basement";
            2: print "lobby";
            3: print "second floor";
            4: print "third floor";
            5: print "fourth floor";
            6: print "fifth floor";
            7: print "sixth floor";
            8: print "seventh floor";
        }
        print " of the Computer Center. An elevator and call button";
        if (self.u_to ~= 0 && self.d_to ~= 0) print "s are";
        else print " is";
        print " on the south side of the hallway. Stairs also lead ";
        if (self.u_to ~= 0)
           { print "up"; if (self.d_to ~= 0) print " and "; }
        if (self.d_to ~= 0) print "down";
        ", for the energetic.";
     ],
     before
     [; Go: if (noun == in_obj) << Go s_obj >>;
            if (noun == s_obj)
               { if (lift.number ~= self.number) "The lift isn't here."; }
     ],
     s_to liftdoor,
 has light;

! ----------------------------------------------------------------------------
!  The map
! ----------------------------------------------------------------------------

Stairs Seventh_Floor "Seventh Floor"
 with number 8,
      d_to Sixth_Floor;
Stairs Sixth_Floor "Sixth Floor"
 with number 7,
      u_to Seventh_Floor, d_to Fifth_Floor;
Stairs Fifth_Floor "Fifth Floor"
 with number 6,
      u_to Sixth_Floor, d_to Fourth_Floor;
Stairs Fourth_Floor "Fourth Floor"
 with number 5,
      u_to Fifth_Floor, d_to Third_Floor;
Stairs Third_Floor "Third Floor"
 with number 4,
      u_to Fourth_Floor, d_to Second_Floor;
Stairs Second_Floor "Second Floor"
 with number 3,
      u_to Third_Floor, d_to Computer_Center;
Stairs Computer_Center "Computer Center"
 with number 2,
      u_to Second_Floor, d_to Basement;
Stairs Basement "Basement"
 with number 1,
      u_to Computer_Center;

! ----------------------------------------------------------------------------
!  The call buttons
! ----------------------------------------------------------------------------

Object UpButton "up-arrow button"
 with name "u" 'call' 'button' 'buttons' 'arrow' 'up' 'up-arrow',
      description
      [; if (UpButtons -> parent(self).number == 0)
            "You see nothing special about ", (the) self, ".";
         print_ret (The) self, " is glowing.";
      ],
      before
      [; Push:
           if (UpButtons -> parent(self).number == 1)
              { print_ret (The) self, " is already activated."; }
           if (lift.number==parent(self).number && lift hasnt general)
              { OpenElevDoor(); return; }
           UpButtons -> parent(self).number = 1;
           if (elevDest==0) elevDest=parent(self).number;
           print_ret (The) self, " begins to glow.";
        ],
     found_in Sixth_Floor Fifth_Floor Fourth_Floor Third_Floor Second_Floor
              Computer_Center Basement,
 has static concealed;

Object DownButton "down-arrow button"
 with name "d" 'call' 'button' 'buttons' 'arrow' 'down' 'down-arrow',
      description
      [; if (DownButtons -> parent(self).number == 0)
            "You see nothing special about ", (the) self, ".";
         print_ret (The) self, " is glowing.";
      ],
      before
      [; Push:
           if (DownButtons -> parent(self).number == 1)
              { print_ret (The) self, " is already activated."; }
           if (lift.number==parent(self).number && lift hasnt general)
              { OpenElevDoor(); return; }
           DownButtons -> parent(self).number = 1;
           if (elevDest==0) elevDest=parent(self).number;
           print_ret (The) self, " begins to glow.";
     ],
     found_in Seventh_Floor Sixth_Floor Fifth_Floor Fourth_Floor
              Third_Floor Second_Floor Computer_Center,
 has static concealed;

! ----------------------------------------------------------------------------
!  The elevator doors
! ----------------------------------------------------------------------------

Object liftdoor "elevator doors"
 with name 'lift' 'elevator' 'door' 'doors' 'liftdoor',
      description
      [; print (The) self, " are ";
         if (self has open) "opened.";
         "closed.";
      ],
      number 0,
      door_dir
      [; if (parent(self) ~= lift)
            return s_to;
         return n_to;
      ],
      door_to
      [; if (parent(self) ~= lift) return lift;
         switch(lift.number)
         {   1: return Basement;
             2: return Computer_Center;
             3: return Second_Floor;
             4: return Third_Floor;
             5: return Fourth_Floor;
             6: return Fifth_Floor;
             7: return Sixth_Floor;
             8: return Seventh_Floor;
         }
      ],
      before
      [; Open, Push, Pull:
            if (self has open) "But the doors are already open!";
            if (self in lift)
               { if (lift has general)
                    "You should wait until the elevator reaches
                    its destination.";
                 "Just press the 'open' button.";
               }
            if (lift.number == parent(self).number)
               "The elevator is here, so the doors will open when you
               press a call button.";
            "You better call the elevator first.";
         Close:
            if (self hasnt open) "But the doors are already closed!";
            if (self in lift)
               "Just press the 'close' button.";
            if (lift.number == parent(self).number)
               "Don't worry, the doors will be closed automatically.";
      ],
      found_in Seventh_Floor Sixth_Floor Fifth_Floor Fourth_Floor
              Third_Floor Second_Floor Computer_Center Basement lift,
         ! Since the doors are implemented as one single floating object
         ! we have to make sure that they don't open all at once. Thus:
      react_before
      [; if (self has general && (parent(self) == lift ||
            parent(self).number == lift.number))
            give self open;
         else give self ~open;
         rfalse;
      ],
      daemon
      [; self.number--;
         if (self.number == 0) CloseElevDoor(1);
      ],
  has door static scenery pluralname; ! general when open

[ OpenElevDoor i;
   liftdoor.number = 3;
   startdaemon(liftdoor);
   give liftdoor general;
   if (location == lift || location.number == lift.number)
      { if (i == 1) print "^";
        print "The elevator doors slide open.^";
      }
   return;
];

[ CloseElevDoor i;
   stopdaemon(liftdoor);
   give liftdoor ~general;
   if (location == lift || location.number == lift.number)
      { if (i == 1) print "^";
        print "The elevator doors slide closed.^";
      }
   return;
];

! ----------------------------------------------------------------------------
!  The elevator itself
! ----------------------------------------------------------------------------

Object lift "Elevator"
 with number 3,                  ! start at 2nd floor
      name 'fake' 'wood' 'wooden' 'walls' 'graffiti' 'graffito',
      description
      [; print "A battered, rather dirty elevator. The fake wood walls are
          scratched and marred with graffiti. A digital sign reads ";
         if (self.number==1) print "B";
         else print self.number-1;
         print ". The elevator doors are ";
         if (liftdoor has open) print "open";
         else print "closed";
         print ". To the right of the doors is an area with floor buttons
          (B and 1 through 7), an open button, a close button, a stop
          switch and an alarm button. ";
         << Examine allElevButtons >>;
      ],
      before
      [; Exit: << Go n_obj >>;
      ],
      n_to liftdoor,
      cant_go "The only exit leads north.",
      daemon
      [  i;
         if (elevDest == 0) return;           ! no task defined
         if (liftdoor has general || stopSwitch has on) return;
         elevMoveCounter--;
         switch(elevMoveCounter)
         { 0: OpenElevDoor(1);
              elevDest = ChooseElevDest();
              elevMoveCounter = 4;
           1: if (elevDirection == 1) lift.number++;
              else lift.number--;
              i = CheckElevDest();
                  if (i ~= 0) elevDest = i;
              PrintElevMessage();
              if (lift.number ~= elevDest)     ! task not finished
                 { elevMoveCounter = 3; return; }
              ElevButtons -> lift.number = 0;
              UpButtons -> lift.number = 0; DownButtons -> lift.number = 0;
              give self ~general;
           2: PrintElevMessage();
           3: give self general;
              if (self.number < elevDest)
                 elevDirection = 1;
              else
                 elevDirection = 2;
              PrintElevMessage();
         }
      ],
  has light;

[ CheckElevDest i;
   i = lift.number;
   if (elevDirection == 1)
      { if (UpButtons -> i == 1 || ElevButtons -> i == 1 )
           return i;
      }
   else
      { if (DownButtons -> i == 1 || ElevButtons -> i == 1)
           return i;
      }
   return 0;
];

[ ChooseElevDest i;
   if (elevDirection == 1)
      { i = SearchUp();
        if (i ~= 0) return i;
        return SearchDown();
      }
   else
      { i = SearchDown();
        if (i ~= 0) return i;
        return SearchUp();
      }
];

[ SearchUp i;
   for (i=lift.number+1:i<=8:i++)
       { if (ElevButtons -> i == 1 || UpButtons -> i == 1)
            return i;
       }
   for (i=8:i>lift.number:i--)
       { if (DownButtons -> i == 1)
            return i;
       }
   return 0;
];

[ SearchDown i;
   for (i=lift.number-1:i>0:i--)
       { if (ElevButtons -> i == 1 || DownButtons -> i == 1)
            return i;
       }
   for (i=1:i<lift.number:i++)
       { if (UpButtons -> i == 1)
            return i;
       }
   return 0;
];

[ PrintElevMessage;
   if (location == lift)
      { switch(elevMoveCounter)
        { 1: print "^The sign flashes briefly. It now displays the ";
             if (lift.number == 1) print "letter B";
             else print "number ", lift.number-1;
             print ".^";
             if (lift.number == elevDest)     ! task finished
                { print "^The elevator slows and comes to a stop. The
                  button for the ";
                  switch(self.number)
                  {   1: print "basement";
                      2: print "lobby";
                      3: print "second floor";
                      4: print "third floor";
                      5: print "fourth floor";
                      6: print "fifth floor";
                      7: print "sixth floor";
                      8: print "seventh floor";
                  }
                  print " blinks off.^";
                }
          2: print "^The elevator continues to move ";
             if (elevDirection == 1) print "up";
             else print "down";
             print "wards.^";
          3: print "^The elevator begins to move ";
             if (elevDirection == 1) print "up";
             else print "down";
             print "wards.^";
        }
      }
   if (location ofclass Stairs)
      { switch(elevMoveCounter)
        { 1: if (lift.number ~= elevDest)
                print "^You hear the elevator moving.^";
             else
                { print "^You no longer hear the elevator moving.^";
                  if (location.number == lift.number)
                     { if (UpButtons -> lift.number == 1 ||
                           DownButtons -> lift.number == 1)
                           { print "^The call button";
                             if (UpButtons -> lift.number == 1 &&
                                 DownButtons -> lift.number == 1)
                             print "s blink";
                             else print " blinks";
                             print " off.^";
                           }
                     }
                }
          2: print "^You hear the elevator moving.^";
          3: print "^You hear the elevator begin moving.^";
        }
      }
];

Object -> sign "sign"
 with name 'digital' 'sign' 'display',
      description
      [; print "The sign reads ";
         if (lift.number==1) print "B";
         else print lift.number-1;
         print_ret ".";
      ],
  has static concealed;

! ----------------------------------------------------------------------------
!  Class definition: floor buttons in the elevator
! ----------------------------------------------------------------------------

Class ElevButton
 has static concealed
with name 'button',
     number 0,
     description
     [; if (ElevButtons -> self.number == 0)
           "You see nothing special about ", (the) self, ".";
        print_ret (The) self, " is glowing."; ],
     before
     [; Push:
          if (ElevButtons -> self.number == 1)
             { print_ret (The) self, " is already activated."; }
          if (self.number == lift.number && lift hasnt general)
             << Push openButton >>;
          ElevButtons -> self.number = 1;
          if (elevDest == 0) elevDest = self.number;
          print "The button for the ";
          switch(self.number)
          {   1: print "basement";
              2: print "lobby";
              3: print "second floor";
              4: print "third floor";
              5: print "fourth floor";
              6: print "fifth floor";
              7: print "sixth floor";
              8: print "seventh floor";
          }
          " begins to glow.";
        ];

! ----------------------------------------------------------------------------
!  A meta object and the floor buttons
! ----------------------------------------------------------------------------

Object -> allElevButtons "buttons"
 with name 'buttons' 'controls' 'area',
      description
      [ i j temp;
        for (i=1:i<=8:i++) {
             if (ElevButtons -> i==1) j++;
        }
        if (j==0) "None of the buttons is activated.";
        print "You notice that ", (number) j, " floor button";
        if (j>1) print "s";
        print " (namely ";
        temp=j;
        for (i=1:i<=8:i++)
            { if (ElevButtons -> i==1)
                 { if (i==1) print "B";
                   else print i-1;
                   temp--;
                   if (temp>0)
                      { if (temp==1) print " and ";
                        else print ", ";
                      }
                 }
            }
        print ") ";
        if (j>1) print "are";
        else print "is";
        " currently activated.";
      ],
      before [; Push: "Only one at a time."; ],
  has static concealed pluralname;

ElevButton -> elevButton1 "basement button"
 with name "b" 'basement' 'cellar',
      number 1;
ElevButton -> elevButton2 "1 button"
 with name "1" 'one' 'lobby' 'first',
      number 2;
ElevButton -> elevButton3 "2 button"
 with name "2" 'two' 'second',
      number 3;
ElevButton -> elevButton4 "3 button"
 with name "3" 'three' 'third',
      number 4;
ElevButton -> elevButton5 "4 button"
 with name "4" 'four' 'fourth',
      number 5;
ElevButton -> elevButton6 "5 button"
 with name "5" 'five' 'fifth',
      number 6;
ElevButton -> elevButton7 "6 button"
 with name "6" 'six' 'sixth',
      number 7;
ElevButton -> elevButton8 "7 button"
 with name "7" 'seven' 'seventh',
      number 8;

! ----------------------------------------------------------------------------
!  and more elevator controls
! ----------------------------------------------------------------------------

Object -> openButton "open button"
 with name 'button' 'open',
      before
      [; Push:
           if (liftdoor has general) "Nothing happens.";
           if (lift has general) "Not between floors.";
           OpenElevDoor(); return;
      ],
  has static concealed;

Object -> closeButton "close button"
 with name 'button' 'close',
      before
      [; Push:
           if (liftdoor hasnt general) "Nothing happens.";
           CloseElevDoor(); return;
      ],
  has static concealed;

Object -> alarmButton "alarm button"
 with name 'button' 'alarm' 'bell',
      before
      [; Push: "For as long as you hold the button down, an
               ear-splitting alarm bell rings.";
      ],
  has static concealed;

Object -> stopSwitch "stop switch"
 with name 'stop' 'switch',
      before
      [; Push, Turn: if (self has on) << SwitchOff self >>;
                     else << SwitchOn self >>;
      ],
      after
      [; SwitchOn:
           print "You turn the switch";
           if (lift has general) " and the elevator comes to a halt.";
           print_ret ".";
         SwitchOff:
           print "You release the switch";
           if (lift has general) " and the elevator starts moving.";
           print_ret ".";
      ],
  has static concealed switchable;

[ Initialise;
 location = Second_Floor; startdaemon(lift); startdaemon(Fred);
 print "^^^^Welcome to the...^^";
 return 2;                           ! don't print headlines pp.
];

[ CallSub;
   if (noun has animate)
       print_ret "You'll have to address ", (the) noun, " directly.";
   print_ret "You can't talk to ", (the) noun, ".";
];

[ CallElevSub i;
   if (location == lift) "You're in it!";
   if (location ofclass Stairs)
      { if (liftdoor has open) "It's right here!";
        if (location == Seventh_Floor or Basement) i = 1;
        print "You'll have to press ";
        if (i ~= 1) print "one of ";
        print "the call button";
        if (i ~= 1) print "s (up or down)";
        print_ret ".";
      }
   "You can't see any elevator here.";
];

Include "Grammar";

Verb 'call'
               * 'elevator'/'lift'              -> CallElev
               * noun                           -> Call;


end;