! ----------------------------------------------------------------------------
!  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
!  mbraun@compuserve.com
!
! ----------------------------------------------------------------------------

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;



