!---------------------------------------------------------------------------!
! !
! 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: !
!
[email protected]. !
! -- 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;