!---------------------------------------------------------------------------!
!                                                                           !
! This file is intended as an illustration of the rather cryptic line in    !
! the Designer's Manual about using the "number" property of the compass    !
! directions to code a maze.  This maze copies the "Alike_Maze" from        !
! Adventure (specifically, from Graham Nelson's Inform port thereof) and    !
! implements it with two rooms: Maze_Room and Maze_Dump, and three          !
! routines:  LoadMaze, SetMaze and MoveInMaze.                              !
!                                                                           !
! This method uses the "number" property to keep track of the location of   !
! objects in the maze, which has two major implications:  1. every object   !
! which the player can drop in the maze MUST have the number property       !
! defined; and 2. the number property cannot be used for anything else.     !
! The latter is avoidable by defining another property, say "maze_number,"  !
! and using it instead.                                                     !
!                                                                           !
! Although this method works fairly well, there are three caveats:          !
! 1. "Places" will list the entire maze as one room (in this case,          !
! "Standard Maze").  This may or may not be desirable.                      !
! 2. "Objects" will list objects dropped in the last room visited properly, !
! but anything in Maze_Dump will be listed as "lost."  This is probably     !
! correctable.                                                              !
! 3.  This method is NOT recommended if you have any NPCs who can wander    !
! around in the maze.  While you COULD keep track of each NPC's location    !
! with his or her "number" property, you'd have to code the movement rules  !
! completely differently.                                                   !
!                                                                           !
! I don't have a great deal of experience with Inform (and in real life I   !
! use COBOL), so there may be better and easier alternatives to some of     !
! this.  I welcome any and all comments, which you can e-mail to me at:     !
! blore@ibm.net.                                                            !
!                   -- Steven Howard, 6/4/1996                              !
!---------------------------------------------------------------------------!                                                                           

Switches dxs;

Constant DEBUG;

Constant Story "The Famous One-Room Maze";
Constant Headline "^An Interactive Labyrinth ^\
        Copyright 1996 by Steven Howard, but freely usable.  ^";

Include "Parser";
Include "Verblib";

!---------------------------------------------------------------------------!
!  First, the routines:  LoadMaze, SetMaze and MoveInMaze                   !
!---------------------------------------------------------------------------!

[ LoadMaze i obj;                     
  for (obj = selfobj + 1: obj <= top_object: obj++)
      {if (obj in Maze_Room)
        {if (obj.&number == 0) 
                print "*** ERROR: Can't move ", (the) obj, ". ***";
         else
                {obj.number = Maze_Room.number;
                 move obj to Maze_Dump;};
         };
       if (obj in Maze_Dump && obj.number == i)
           move obj to Maze_Room;
      };
  Maze_Room.number = i;
  ];
  
!---------------------------------------------------------------------------!
!  LoadMaze is called before the player moves into a new room in the        !
!  maze.  It moves items currently on the floor to the holding area         !
!  Maze_Dump, using each object's "number" property to store its            !
!  "real location".  Meanwhile, it moves any objects in Maze_Dump whose     !
!  number property is equal to the new room number into Maze_Room.          !
!  Finally, it sets the number property of Maze_Room to the new value.      !
!---------------------------------------------------------------------------!

[ SetMaze ;  
  n_obj.number=0;
  ne_obj.number=0;
  e_obj.number=0;
  se_obj.number=0;
  s_obj.number=0;
  sw_obj.number=0;
  w_obj.number=0;
  nw_obj.number=0;
  u_obj.number=0;
  d_obj.number=0;
  out_obj.number=0;
  switch (Maze_Room.number) {
               1: {n_obj.number = 1;
                   e_obj.number = 2;
                   s_obj.number = 4;
                   w_obj.number = 11;
                   u_obj.number = 100;};
               2: {e_obj.number = 4;
                   s_obj.number = 3;
                   w_obj.number = 1;};
               3: {n_obj.number = 22;
                   e_obj.number = 2;
                   s_obj.number = 6;
                   d_obj.number = 17;};
               4: {n_obj.number = 2;  
                   e_obj.number = 15;
                   s_obj.number = 16;
                   w_obj.number = 1;
                   u_obj.number = 14;
                   d_obj.number = 14;};
               5: {e_obj.number = 6;
                   w_obj.number = 7;};
               6: {e_obj.number = 3;
                   s_obj.number = 8;
                   w_obj.number = 5;
                   d_obj.number = 7;};
               7: {e_obj.number = 8;
                   s_obj.number = 9;
                   w_obj.number = 5;
                   u_obj.number = 6;};
               8: {n_obj.number = 10;
                   e_obj.number = 7;
                   s_obj.number = 8;
                   w_obj.number = 6;
                   u_obj.number = 9;
                   d_obj.number = 24;};
               9: {n_obj.number = 8;
                   s_obj.number = 18;
                   w_obj.number = 7;};
              10: {n_obj.number = 10;
                   e_obj.number = 101;
                   w_obj.number = 8;
                   d_obj.number = 19;};
              11: {n_obj.number = 1;
                   e_obj.number = 21;
                   s_obj.number = 11;
                   w_obj.number = 11;};
              12: {e_obj.number = 13;
                   s_obj.number = 101;
                   w_obj.number = 23;};
              13: {n_obj.number = 101;
                   w_obj.number = 12;
                   nw_obj.number = 25;};
              14: {u_obj.number = 4;
                   d_obj.number = 4;};
              15: {w_obj.number = 4;
                   out_obj.number = 4;};
              16: {w_obj.number = 4;
                   out_obj.number = 4;};
              17: {u_obj.number = 3;
                   out_obj.number = 3;};
              18: {w_obj.number = 9;
                   out_obj.number = 9;};
              19: {u_obj.number = 10;
                   out_obj.number = 10;};
              20: {e_obj.number = 101;
                   out_obj.number = 101;};
              21: {w_obj.number = 11;
                   out_obj.number = 11;};
              22: {s_obj.number = 3;
                   out_obj.number = 3;};
              23: {e_obj.number = 12;
                   out_obj.number = 12;};
              24: {u_obj.number = 8;
                   out_obj.number = 8;};
              25: {se_obj.number = 13;
                   out_obj.number = 13;};
             };
  ];
!---------------------------------------------------------------------------!                                                                           
!  SetMaze is called whenever the player enters a new room in the maze.     !
!  The number property for the compass directions follow a simple code:     !
!  0 means movement in this direction is not allowed; 1 - 99 means          !
!  movement in this direction leads into room 1 - 99 of the maze, as        !
!  appropriate; 100 or more means movement in this direction leads out      !
!  of the maze.  Obviously, a gigantic maze of more than 99 rooms would     !
!  require changing this scheme somewhat.                                   !
!---------------------------------------------------------------------------!                                                                           

[ MoveInMaze ;
        if (noun.number == 0) return 0;
        if (noun.number == 100) return Starting_Room;
        if (noun.number == 101) return At_Brink_Of_Pit;
        return Maze_Room; 
];
 
!---------------------------------------------------------------------------!                                                                           
! MoveInMaze "translates" the numbers assigned by SetMaze.  Making it       !
! a separate routine like this saves typing.                                !
!---------------------------------------------------------------------------!                                                                           


Object Starting_Room "Starting Room"
        with name "starting" "room" "cavern" "stone" "suspiciously" 
            "twisty" "passage" "south",
        description "You are standing in a stone cavern.  A \
            suspiciously twisty passage leads south.",
        s_to Maze_Room,
        in_to Maze_Room,
        before [ ;
                Go:  if (noun == s_obj or in_obj
                                && Maze_Room.number ~= 1) 
                        {LoadMaze(1);
                         rfalse;};
               ];
       
!---------------------------------------------------------------------------!                                                                           
! An "ordinary" room just outside the maze.  The before rule makes sure the !      
! player enters the right room in the maze.  It's not strictly necessary    !
! here, since the maze is defined with number = 1 and the only way to get   !
! here is from Room 1, but this situation is unusual.  At_Brink_of_Pit,     !
! below, is an example of why this type of rule is needed.                  !
!---------------------------------------------------------------------------!                                                                           

!---------------------------------------------------------------------------!                                                                           
! To help map the maze, we give the player a bunch of "things" to drop:     !
!---------------------------------------------------------------------------!                                                                           

Nearby red_thing "red thing"
        with name "red" "thing",
        description "It's a red thing.  You wouldn't understand.",
        number 0;

Nearby blue_thing "blue thing"
        with name "blue" "thing",
        description "It's a blue thing.  You wouldn't understand.",
        number 0;

Nearby yellow_thing "yellow thing"
        with name "yellow" "thing",
        description "It's a yellow thing.  You wouldn't understand.",
        number 0;
       
Nearby green_thing "green thing"
        with name "green" "thing",
        description "It's a green thing.  You wouldn't understand.",
        number 0;
        
Nearby purple_thing "purple thing"
        with name "purple" "thing",
        description "It's a purple thing.  You wouldn't understand.",
        number 0;
        
Nearby orange_thing "orange thing"
        with name "orange" "thing", article "an",
        description "It's an orange thing.  You wouldn't understand.",
        number 0;

!---------------------------------------------------------------------------!                                                                           
! And now, the maze itself:                                                 !
!---------------------------------------------------------------------------!                                                                           

Object Maze_Room "Standard Maze"
        with name "maze" "passage" "passages" "little" "twisty"
                "standard" "dead" "end",
        description 
                [; 
                if (self.number < 15)
                      "You are in the standard maze of twisty little \
                      passages.";
                if (self.number == 25)
                      "This is the pirate's dead end.";
                "You have reached a dead end.";],
        short_name [ ;
                if (action == ##Places or ##Objects) rfalse;
                if (self.number < 15) {print "Standard Maze";
                        #IFDEF DEBUG; print " (", self.number, ")";#ENDIF;
                        rtrue};                    
                print "Dead End";
                #IFDEF DEBUG; print " (", self.number, ")";#ENDIF;
                rtrue;                    
                ],
        number 1,               
        n_to   MoveInMaze,
        ne_to  MoveInMaze,
        e_to   MoveInMaze,
        se_to  MoveInMaze,
        s_to   MoveInMaze,
        sw_to  MoveInMaze,
        w_to   MoveInMaze,
        nw_to  MoveInMaze,
        u_to   MoveInMaze,
        d_to   MoveInMaze,
        out_to MoveInMaze,
        cant_go [; if (noun==out_obj && self.number < 15)
                        "Easier said than done.";
                   if (self.number > 14)
                        "You'll have to go back the way you came.";
                   "You can't go that way.";],
        before [ ;
                Go:   if (noun.number == 0 || noun.number > 99) rfalse;
                      if (self.number ~= noun.number) 
                         LoadMaze (noun.number);
               ],
        after [ ;
                Go: SetMaze();
                    if (self has general) <<Look>>;
                    give self general;
              ];

!---------------------------------------------------------------------------!                                                                           
! number holds the current room number in the maze.  Rooms 1 through 14     !
! represent the maze proper (i.e. those rooms whose description in the      !
! original is "You are in a maze of twisty little passages, all alike").    !
! Rooms 15 through 24 are the ordinary dead ends (i.e. those rooms whose    !
! description is "You have reached a dead end" and where "out" takes you    !
! to the previous room).  Room 25 is the pirate's dead end.                 !
!                                                                           !
! The before rule on "Go" calls "LoadMaze" if the player is moving          !
! within the maze.                                                          !
!                                                                           !
! Somewhat deceptively, moving within the maze also causes a "Look"         !
! action, making every room appear to have never been visited before.       !
! This could be improved by keeping track of which rooms have already       !
! been visited and only doing a <<Look>> in those which have not.           !
!---------------------------------------------------------------------------!                                                                           

Object Maze_Dump "Maze Dump";

Nearby plaid_thing "plaid thing"
        with name "plaid" "thing",
        description "It's a plaid thing.  You wouldn't understand.",
        number 2;

Nearby treasure_chest "treasure chest"
        with name "strangely" "familiar" "treasure" "chest",
        initial "You have found the treasure chest!",
        description "It's strangely familiar . . .",
        number 25;

!---------------------------------------------------------------------------!                                                                           
! Maze_Dump isn't connected to any other rooms in the game.  It's just a    !
! holding place for items dropped in the maze which aren't in the current   !
! room.                                                                     !
! There are two things left lying around in the maze.  A "plaid thing"      !
! has been dropped in the second room, and the pirate's chest is in its     !
! expected location.                                                        !
!---------------------------------------------------------------------------!                                                                           

Object At_Brink_of_Pit "At Brink of Pit"
        with name "twisty" "passages" "passage" "maze" "guard" "rail",
        description "You are at the brink of a thirty-foot pit. \
                A newly-installed guard rail prevents climbing down.  \
                Those twisty passages continue off in all directions.",
        d_to "The guard rail prevents it.",
        w_to Maze_Room,
        n_to Maze_Room,
        e_to Maze_Room,
        s_to Maze_Room,
        before [ ;
                Go:  if (noun == s_obj && Maze_Room.number ~= 20) 
                        {LoadMaze(20);
                         rfalse;};
                     if (noun == n_obj && Maze_Room.number ~= 12)    
                        {LoadMaze (12);
                         rfalse;};
                     if (noun == e_obj && Maze_Room.number ~= 13) 
                        {LoadMaze (13);
                         rfalse;};
                     if (noun == w_obj && Maze_Room.number ~= 10)
                        {LoadMaze (10);
                         rfalse;};
                ];

!---------------------------------------------------------------------------!                                                                           
! This illustrates the use of the before rule on "Go" to handle the case    ! 
! where the player leaves the maze and later enters a different room.       !
!---------------------------------------------------------------------------!                                                                           

Nearby pit "pit"
        with name "brink" "of" "pit",
        description "It's a long way down.  Luckily, the sturdy \ 
                guard rail prevents anyone from trying to climb down.",
        before [ ;
                Climb:  "The guard rail prevents it.";
               ],
        has scenery;

!---------------------------------------------------------------------------!                                                                           
! In Adventure the pit is an exit.  Here, it's just window dressing.        !
!---------------------------------------------------------------------------!                                                                           

Object glow_stick "glow stick"
        with name "glow" "stick" "luminous" "tube" "green" "fluid"
        "plastic" "of",
        description "A plastic tube of luminous green fluid.",
        number 0,
        has light;

!---------------------------------------------------------------------------!                                                                           
! Of course, the player needs to see.  This also illustrates why it's       !
! necessary to actually move the player from Maze_Room to Maze_Room.  At    !
! first thought, it seems we could accomplish the same thing by calling     !
! LoadMaze, calling SetMaze, performing a Look action and returning true    !
! in the before routine for Maze_Room.  But that wouldn't handle the player !
! dropping the only light source in the maze.                               !
!---------------------------------------------------------------------------!                                                                           

Include "Grammar";

[ Initialise;
        location = Starting_Room;
        move glow_stick to player;
        "^^^^^^^You've seen this maze before.  Now, go find that \
         pirate chest.^";
        
];

end;

