Elevator and stairs (Dialog example)

From IFWiki

(current player #player)
(#player is #in #ground)

(story author) IF Community

%% Creating the right predicates makes for simple, elegant code.

(level #ground 1)
(level #external-button-ground 1)
(level #internal-button-g 1)
(level #ground-door 1)
(level #second 2)
(level #external-button-second 2)
(level #second-door 2)
(level #internal-button-2 2)
(level #third 3)
(level #external-button-third 3)
(level #third-door 3)
(level #internal-button-3 3)

($Button is button of $Room)
  *(button $Button)
  *(room $Room)
  (level $Room $N)
  (level $Button $N)

($Door is door of $Room)
  *(door $Door)
  *(room $Room)
  (level $Door $N)
  (level $Room $N)

%% Floors

#ground
(room *)
(name *) ground floor
(singleton *)
(ordinal *) ground

%% Instead of creating three separate stairs objects, we will create one and
%% specify that each of the three floors attracts it.
(* attracts #stairs)
(look *)
  (look at *) and the stairs are up.
(from * go #north through #ground-door to #elevator)
  (#elevator is on *)
(from * go #up to #second)

#second
(room *)
(name *) second floor
(singleton *)
(ordinal *) second
(* attracts #stairs)
(look *)
  (look at *) and stairs go both up and down from here.
(from * go #north through #second-door to #elevator)
  (#elevator is on *)
(from * go #up to #third)
(from * go #down to #ground)

#third
(room *)
(name *) third floor
(singleton *)
(ordinal *)
(* attracts #stairs)
(look *)
  (look at *) or you can take the stairs down.
(from * go #north through #third-door to #elevator)
  (#elevator is on *)
(from * go #down to #second)

(look at $Room)
  ($Door is door of $Room)
  You are on the (ordinal $Room) floor. The
  (if)($Door is open)(then)
    open
  (else)
    closed
  (endif)
  door to the elevator is north,

%% Call Buttons

#external-button-ground
(external button *)
(* is #in #ground)

#external-button-second
(external button *)
(* is #in #second)

#external-button-third
(external button *)
(* is #in #third)

(name (external button $Button))
  (external button $Button)
  call button

(descr (external button $Button))
  (current room $Room)
  ($Button is button of $Room)
  Pushing this button summons the elevator to this floor. It's currently
  (if)($Button is lit)(then)
    lit
  (else)
    dark
  (endif)
  .

(perform [push (button $Button)])
  (push $Button)

%% When the elevator is already on the floor and the door is closed
(push $Button)
  (#elevator is on $Origin)
  ($Button is button of $Dest)
  ($Origin = $Dest)
  ($Door is door of $Origin)
  ($Door is closed)
  (now)($Door is open)
  (now)($Door closes in 2)
  (The $Door) opens.

%% When the elevator is already on the floor and the door is already open
(push $Button)
  (#elevator is on $Origin)
  ($Button is button of $Dest)
  ($Origin = $Dest)
  ($Door is door of $Origin)
  ($Door is open)
  Nothing happens.
  ($Door closes in $Old)
  ($Old plus 1 into $New)
  (now)($Door closes in $New)

%% When the elevator is on a different floor and all the doors are closed
(push $Button)
  *(external button $External)
  ($Button is button of $Room)
  ($External is button of $Room)
  (#ground-door is closed)
  (#second-door is closed)
  (#third-door is closed)
  (now)(#elevator has destination $Room)
  (now)~(#external-button-ground is lit)
  (now)~(#external-button-second is lit)
  (now)~(#external-button-third is lit)
  (now)($External is lit)
  (if)(current room $Room)(or)(current room #elevator)(then)
    The call button lights up.
  (endif)
  (move elevator)

%% When the elevator is on a different floor and at least one door is open
(push $Button)
  *(external button $External)
  ($Button is button of $Dest)
  ($External is button of $Dest)
  (now)(#elevator has destination $Dest)
  (now)~(#external-button-ground is lit)
  (now)~(#external-button-second is lit)
  (now)~(#external-button-third is lit)
  (now)($External is lit)
  (if)(current room $Dest)(or)(current room #elevator)(then)
    The call button lights up.(par)
  (endif)
  (#elevator is on $Room)
  ($Door is door of $Room)
  (if)($Door is open)(then)
    (if)(current room $Room)(or)(current room #elevator)(then)
      (The $Door) closes.
    (endif)
  (endif)
  (now)(#ground-door is closed)
  (now)(#second-door is closed)
  (now)(#third-door is closed)
  (now)~(#ground-door closes in $)
  (now)~(#second-door closes in $)
  (now)~(#third-door closes in $)
  (stop)

%% Elevator Doors

#ground-door
(door *)
(openable *)
(* is closed)
(* is #in #ground)

#second-door
(door *)
(openable *)
(* is closed)
(* is #in #second)

#third-door
(door *)
(openable *)
(* is closed)
(* is #in #third)

(name (door $Door))
  elevator door

(descr (door $Door))
  (current room $Room)
  ($Door is door of $Room)
  The elevator door is
  (if)($Door is open)(then)
    open,
  (else)
    closed,
  (endif)
    and there's a call button beside it.

(prevent [open (door $Door)])
  ($Door is open)
  (The $Door) is already open.

(prevent [open (door $Door)])
  Try pushing the call button.

(prevent [close (door $Door)])
  ($Door is closed)
  (The $Door) is already closed.

(prevent [close (door $Door)])
  (The $Door) will close automatically when it needs to.

%% Elevator

#elevator
(name *) elevator
(room *)
(singleton *)
(* is on #ground)
(look *)
  You are inside the elevator. The door to the south is
  (#elevator is on $Room)
  ($Door is door of $Room)
  (if)($Door is open)(then)
    open
  (else)
    closed
  (endif)
  , and there's the usual panel of buttons beside it.
(from * go #south through #ground-door to #ground)
  (* is on #ground)
(from * go #south through #second-door to #second)
  (* is on #second)
(from * go #south through #third-door to #third)
  (* is on #third)

%% As a kindness to the player we remap OUT to SOUTH.
(from * go #out to #south)

(on every tick)
  ($Door closes in $Old)
  ($Door is door of $Room)
  (if)($Old = 0)(then)
    (now)~($Door closes in $)
    (now)($Door is closed)
    (if)(current room $Room)(or)(current room #elevator)(then)
      (par)The door closes.
    (endif)
  (else)
    ($Old minus 1 into $New)
    (now)($Door closes in $New)
  (endif)

%% If the elevator is already on the right floor
(on every tick)
  (#elevator is on $Current)
  (#elevator has destination $Dest)
  ($Current = $Dest)
  ($Door is door of $Current)
  (now)($Door is open)
  (now)~(#elevator has destination $)
  (now)($Door closes in 2)
  ($Button is button of $Current)
  (now)~($Button is lit)
  (current room $Room)
  (if)($Room = $Current)(or)($Room = #elevator)(then)
    (par)(The $Door) opens. The call button blinks off.
  (endif)

(on every tick)
  (move elevator)

%% If the elevator is below the destination
(move elevator)
  (#elevator is on $Current)
  (#elevator has destination $Dest)
  (level $Current $CurrentLevel)
  (level $Dest $DestLevel)
  ($CurrentLevel < $DestLevel)
  ($CurrentLevel plus 1 into $NextLevel)
  (level $Room $NextLevel)
  (now)(#elevator is on $Room)
  (if)(current room #elevator)(then)
    (par)You feel the elevator going up
    (if)($Dest = $Room)(then)
      , and then it comes to a stop
    (endif)
    .
  (endif)
  (stop)

%% If the elevator is above the destination
(move elevator)
  (#elevator is on $Current)
  (#elevator has destination $Dest)
  (level $Current $CurrentLevel)
  (level $Dest $DestLevel)
  ($CurrentLevel > $DestLevel)
  ($CurrentLevel minus 1 into $NextLevel)
  (level $Room $NextLevel)
  (now)(#elevator is on $Room)
  (if)(current room #elevator)(then)
    (par)You feel the elevator going down
    (if)($Dest = $Room)(then)
      , and then it comes to a stop
    (endif)
    .
  (endif)
  (stop)

%% Panel

#panel
(name *) panel
(* is #in #elevator)
(descr *)
  The panel has three buttons: G, 2, and 3. There is also a LED display that
  reads (display floor).

#internal-button-g
(name *) g button
(dict *)
(plural dict *) buttons
(internal button *)
(* is #partof #panel)

#internal-button-2
(name *) 2 button
(dict *) two
(plural dict *) buttons
(internal button *)
(* is #partof #panel)

#internal-button-3
(name *) 3 button
(dict *) three
(internal button *)
(plural dict *) buttons
(* is #partof #panel)

%% We make external buttons and internal buttons members of the same
%% class. This allows us to make our code more concise elsewhere.
(button $Obj) *(external button $Obj)
(button $Obj) *(internal button $Obj)

%% Instead of creating a distinct "buttons" object, we create a means to refer
%% to multiple already existing objects.

(action [examine $] may group (internal button $) with (internal button $))

(group-perform [examine $List])
  *(internal button $Obj)
  ($Obj is one of $List)
  (try [examine #panel])

(instead of [examine (internal button $)])
  (try [examine #panel])

%% Display

#display
(name *) LED display
(singleton *)
(descr *)
  The LED display is showing a large red (display floor).(par)
(* is #in #elevator)

(display floor)
  (#elevator is on #ground)
  G

(display floor)
  (#elevator is on #second)
  2

(display floor)
  3

%% Stairs

#stairs
(name *) stairs
(plural *)
(singleton *)
(descr *)
  (current room $Room)
  (if)($Room = #ground)(then)
    They go up.(par)
  (elseif)($Room = #second)(then)
    They go up and down.(par)
  (else)
    They go down.(par)
  (endif)

(instead of [take/climb #stairs])
  (current room $Room)
  ($Room is one of [#ground #second #third])
  (if)($Room = #ground)(then)
    (try [go #up])
  (elseif)($Room = #third)(then)
    (try [go #down])
  (else)
    Which way do you want to go?(par)
    1. Up.(line)
    2. Down.(par)
    (get key $Key)
    (handle keypress $Key)
  (endif)

(handle keypress $Key)
  ($Key is one of [1 @\u])
  (try [go #up])

(handle keypress $Key)
  ($Key is one of [2 @\d])
  (current room $Room)
  (try [leave $Room #down])

(perform [take #stairs])
  There are no stairs here.(par)

%% As per our instructions, we make going up or down stairs take three ticks.
(after [go $Dir])
  ($Dir is one of [#up #down])
  (tick)(tick)

%% TAKE <obj> <dir>

(grammar [take [object] [direction]] for [take $ $])

(instead of [take $Obj $Dir])
  ($Obj = #stairs)
  ($Dir is one of [#up #down])
  (try [go $Dir])