! This is not a game in the real sense.
! This is only a demonstration of one way to implement an
! AutoMap Feature in Inform.
! Feel free to copy and paste large portions of this code into
! your game. Feel free to translate this demonstration for
! Inform 6 or for other versions of Inform. Feel free to
! add the name of the translator to the Copyright line in
! that case, as well as additional comments.
! Feel free to just improve this demonstration, tagging on
! "Edited by (whoever)" in an appropriate place.
! Feel free to make a standard library out of it, too.
! Feel free to post any of the above (including just this
! source code) to the if-archive.
! Basically, consider this to be pretty much public domain.
! The original author may be contacted via
[email protected]
! or the following address:
! Nathan Eady
! 307 Gill Ave
! Galion OH 44833
! United States of America
Switches pv5rsxz; ! Everyday use.
Constant Story "^Automatic Mapping";
Constant Headline "^A Somewhat Interactive Demonstration.^\
Copyright (c) 1998 by Belly Laugh Software and Nathan Eady.^";
Release 2; Serial "980717";
! Constant DEBUG 1;
Constant MAX_CARRIED 100;
Property area 0;
Constant NO_AREA 0;
Constant MAIN_AREA 1;
Constant UPSTAIRS_AREA 2;
Constant MAX_MAP_X 5;
Constant MAX_MAP_Y 5;
Property MapNum; ! Holds the number of the room on the map.
Property MapDir;
! MapDir should contain, if anything, a routine, which should
! examine automapfeature.number for a direction property
! (*_to) and return
! true if the direction should be mapped as a travellable
! path, false if not, and -1 to say
! just check the property with ValueOrRun() to see
! if it goes anywhere.
! There is also the option to print any single character
! and then return -2 to so indicate.
! However, when doing this for up, down, in, or out,
! (which are all the same), it should be handled under
! in_to (which should print a character and return -2),
! and out_to should return -2,
! and u_to and d_to must return either 0 or -2.
include "parser";
[ MapLoc secnum x y;
switch (secnum)
{
NO_AREA: return -1;
MAIN_AREA:
switch (10*y + x)
{
12: return RoomOne;
21: return RoomTwo;
22: return junction;
23: return EastOne;
24: return RoomFive;
31: return RoomFour;
32: return SouthOne;
34: return RoomEight;
42: return SouthTwo;
43: return RoomThree;
11, 41, 33: return 0;
default: return -1;
}
UPSTAIRS_AREA:
switch (10*y + x)
{ 11: return RoomSeven;
12: return RoomSix;
13: return FiveTop;
default: return -1;
}
"Can't Happen: MapLoc fell through!";
}
];
! Main Floor:
!
! 1
! |
! |
! 2 -- hallway-- 5*
! a |
! l |
! 4 -- l 8
! w
! a
! y -- 3
!
!=========================================
Class boring
with short_name "Boring, Empty Room",
description "There's nothing to do here; the room only \
exists to ~flesh out~ the map.",
area MAIN_AREA,
has light;
Object junction "Junction"
with description "Hallways lead east and south. \
There are rooms to the north and west.",
n_to RoomOne,
w_to RoomTwo,
e_to EastOne,
s_to SouthOne,
area MAIN_AREA,
MapNum [; print " h"; return -2; ],
MapDir [;
switch (automapfeature.number)
{
n_to, w_to: rtrue;
s_to, in_to: print "a"; return -2;
e_to: print "l"; return -2;
out_to: return -2;
default: return 0;
}
],
after [;
MapInfo:
if (noun~=self) rfalse;
"(Where you started the ~game~.)";
],
has light;
Nearby sign "sign"
with name "sign",
description "~To see a map of the area nearby (so far as you \
have explored it), type MAP at the prompt.~",
has static;
Object EastOne "East End of Hallway"
with description "There is a room to the east. The hallway leads west.",
name "crystal" "staircase",
e_to RoomFive,
w_to junction,
area MAIN_AREA,
MapNum [; print "wa"; return -2; ],
MapDir [;
switch (automapfeature.number)
{
w_to: print "l"; return -2;
in_to: print "y"; return -2;
out_to: return -2;
e_to: rtrue;
default: rfalse;
}
],
has light;
Object RoomFive "Staircase Room (bottom)"
with name "crystal" "staircase",
description "A crystal staircase leads up from here.",
w_to EastOne,
u_to FiveTop,
s_to RoomEight,
area MAIN_AREA,
MapNum 5,
before [;
MapInfo:
if (noun~=self) rfalse;
! I've put this in the before rule just to illustrate.
! The usual information isn't printed, only this:
"The runes on the map next to that location indicate \
a staircase.";
],
has light;
Object SouthOne "North-South Hallway"
with description "There is a room to the west.",
n_to junction,
s_to SouthTwo,
w_to RoomFour,
MapNum [; print " l"; return -2; ],
MapDir [;
switch (automapfeature.number)
{ w_to: rtrue;
n_to: print "l"; return -2;
s_to: print "w"; return -2;
return 0;
}
],
area MAIN_AREA,
has light;
Object SouthTwo "South End of Hallway"
with description "The hallway leads north, and there is a room \
to the east.",
n_to SouthOne,
e_to RoomThree,
MapNum [; print " y"; return -2; ],
MapDir [;
switch (automapfeature.number)
{ e_to: rtrue;
n_to: print "a"; return -2;
return 0;
}
],
area MAIN_AREA,
has light;
Object RoomOne "r"
class boring,
with MapNum 1,
s_to junction;
Object RoomTwo "r"
class boring,
with MapNum 2,
e_to junction;
Object RoomThree "r"
class boring,
with MapNum 3,
w_to SouthTwo;
Object RoomFour "r"
class boring,
with MapNum 4,
e_to SouthOne;
Object RoomEight "r"
class boring,
with MapNum 8,
n_to RoomFive;
! Upstairs:
!
! 7 -- 6*-- 5*
!
!=========================================
Object FiveTop "Staircase Room (top)"
with area UPSTAIRS_AREA,
description "A crystal staircase leads down.",
name "crystal" "staircase",
d_to RoomFive,
w_to RoomSix,
MapNum 5,
after [;
MapInfo:
if (noun~=self) rfalse;
! This appears in an after routine, so the usual
! information (room short name, but you could change
! MapInfoSub) is printed first, followed by this:
"There is also a symbol indicating a staircase.";
],
has light;
Object RoomSix "East-West Hallway"
with area UPSTAIRS_AREA,
description "Aren't this game's descriptions great?",
in_to ClosetDoor,
e_to FiveTop, w_to RoomSeven,
MapNum 6,
has light;
Object ClosetDoor "closet door" RoomSix
with name "closet" "door",
door_dir in_to, door_to Closet,
has open enterable door;
Object Closet "Closet"
with out_to RoomSix,
description "It's a closet.";
Nearby thepoint "point"
with name "point",
description "Did you get the point yet?",
each_turn [; if (score>0) deadflag = 2; ],
has scored;
Object RoomSeven "Lamp Room"
with area UPSTAIRS_AREA,
MapNum 7,
e_to RoomSix,
description "Everything glows brightly here.",
after [; Go: move lamp to player; ],
has light;
Object lamp "lamp"
with name "lamp",
description "It's a lamp. (This game has a talent for stating \
the obvious, doesn't it?)",
has light;
! ---------------------------------------
[ Initialise;
location = junction;
give SouthOne visited;
give SouthTwo visited;
give EastOne visited;
];
include "verblib";
!------------------------------------------------ MapHead
[ MapHead horf a;
switch (a)
{
NO_AREA: if (horf==1)
{ style bold;
print "^MAP OF UNMAPPED REGION:";
style roman;
"^";
}
"^(You are here.)^";
MAIN_AREA:
if (horf==1)
{ style bold; print "MAP OF MAIN FLOOR:"; style roman; "^"; }
"^Bold represents your location; An * indicates that \
non-compass (up, down, in, out) travel may be \
possible. Type MAP INFO ## to look up additional \
information about room number ##.";
UPSTAIRS_AREA:
if (horf==1)
{ style bold; print "MAP OF UPSTAIRS:"; style roman; "^"; }
"^[This footer intentionally left blank.]^";
}
];
[ AboutSub;
Print "This is not a game in the real sense.\
^This is only a demonstration of one way to implement an\
^AutoMap Feature in Inform.\
^\
^Feel free to copy and paste large portions of this code into\
^your game. Feel free to translate this demonstration for\
^Inform 6 or for other versions of Inform. Feel free to\
^add the name of the translator to the Copyright line in\
^that case, as well as additional comments.\
^";
Print "Feel free to just improve this demonstration, tagging on\
^~Edited by (whoever)~ in an appropriate place.\
^\
^Feel free to make a standard library out of it, too.\
^Feel free to post to the if-archive either this source \
^code or anything derived from it.\
^\
^Basically, consider this to be pretty much public domain.\
^\
^The original author may be contacted via jonadab@@64bright.net\
^or the following address:\
^ Nathan Eady\
^ 307 Gill Ave\
^ Galion OH 44833\
^ United States of America";
Print "^^This is Release 1. I plan to implement an ~information~ feature \
to allow the user to look up any of the nodes on the map by \
number and be told (at least) the short name of that location. \
This can be implemented with a fake action so that the location's \
before (or after) rule can then trap it and provide any additional \
information the author wants to give. However, this is not \
included in this first release, since I wanted to get some \
feedback before implementing it, if possible.";
];
!------------------------------------------------ MapDirX
! MapDir should contain, if anything, a routine, which should
! examine automapfeature.number for a direction object (*_obj)
! and return
! true if the direction should be mapped as a travellable
! path, false if not, and -1 to say
! just check the property with ValueOrRun() to see
! if it goes anywhere.
[ MapDirX loc d dir answer;
switch (d)
{ w_obj: dir=w_to;
e_obj: dir=e_to;
n_obj: dir=n_to;
s_obj: dir=s_to;
u_obj: dir=u_to;
d_obj: dir=d_to;
se_obj: dir=se_to;
ne_obj: dir=ne_to;
sw_obj: dir=sw_to;
nw_obj: dir=nw_to;
in_obj: dir=in_to;
out_obj: dir=out_to;
}
automapfeature.number = dir; ! MapDir routines read this value
answer = -1; ! If there's no MapDir routine, it's the same
! as if it returns -1.
if (loc.#MapDir~=0)
{ ! The room provides a routine to tell us.
answer = ValueOrRun(loc, MapDir);
}
if (answer~=-1) return answer; ! Thus, -2 will be returned.
! So if we get here the MapDir routine doesn't help.
! We'll have to examine the property manually.
if (loc.#dir==0) return 0;
if (loc.dir>0) return 1; ! Note that this will cause
! serious run-time problems if
! that property has anything in it
! other than a value.
];
!------------------------------------------------ AutoMapSub
[ AutoMapSub a h v l n p;
a = ValueOrRun(location, area);
MapHead(1, a); ! Print Header
if ( (0->33)<(5*MAX_MAP_X+1) ) ! Check the screen width
! (as the interpreter reports it
! in the header)
print "[Some (or all) maps may be distorted by wrapping on narrow \
displays. Sorry.]";
for (v=1:v<=MAX_MAP_Y:v++) ! VPass, the single vertical line-for-line pass.
{
! First HPass: Upper Connectors
for (h=1:h<=MAX_MAP_X:h++)
{
l = MapLoc (a, h, v);
if ((h==1)&&(l~=-1)) print "^";
if ((l~=0 or -1)&&(l has visited))
{
if (l==location) style bold; else style roman;
p = MapDirX(l, nw_obj);
if (p==1) print "@@92"; else { if (p~=-2) print " "; }
print " ";
p = MapDirX(l, n_obj);
if (p==1) print "|"; else { if (p~=-2) print " "; }
print " ";
p = MapDirX(l, ne_obj);
if (p==1) print "/"; else { if (p~=-2) print " "; }
}
else print " ";
}
! Second HPass: Items & Horiz Connectors
for (h=1:h<=MAX_MAP_X:h++)
{
l = MapLoc (a, h, v);
if ((h==1)&&(l~=-1)) print "^";
if ((l~=0 or -1)&&(l has visited))
{
if (l==location) style bold; else style roman;
p = MapDirX(l, w_obj);
if (p==1) print "-"; else { if (p~=-2) print " "; }
n = ValueOrRun(l, MapNum);
if (n~=-2)
{ if (n<10) print " "; ! MUST NOT BE > 99
! (If you need room numbers from 100 to 999,
! it wouldn't be hard to adjust for that,
! but the depiction of each node would be
! a character wider.)
print n;
}
n = 0;
if (MapDirX(l, u_obj)==1) n++;
if (MapDirX(l, d_obj)==1) n++;
if (MapDirX(l, in_obj)==1) n++;
if (MapDirX(l, out_obj)==1) n++;
if (n>0) print "*";
else { if (MapDirX(l, out_obj)==0) print " "; }
p = MapDirX(l, e_obj);
if (p==1) print "-"; else { if (p~=-2) print " "; }
}
else print " ";
}
! Third HPass: Lower Connectors
for (h=1:h<=MAX_MAP_X:h++)
{
l = MapLoc (a, h, v);
if ((h==1)&&(l~=-1)) print "^";
if ((l~=0 or -1)&&(l has visited))
{
if (l==location) style bold; else style roman;
p = MapDirX(l, sw_obj);
if (p==1) print "/"; else { if (p~=-2) print " "; }
print " ";
p = MapDirX(l, s_obj);
if (p==1) print "|"; else { if (p~=-2) print " "; }
print " ";
p = MapDirX(l, se_obj);
if (p==1) print "@@92"; else { if (p~=-2) print " "; }
}
else print " ";
}
}
style roman;
MapHead(2, a); ! Print Footer
];
!------------------------------------------------ MapInfoSub
[ MapInfoSub;
if (BeforeRoutines()==1) rfalse; ! we have to do this
! explicitly since AutoMapSub
! is on the same verb & we want
! that to be meta. If your
! verb isn't meta, remove
! this call to BeforeRoutines.
if (noun==0)
"There's no such number on the map at the moment.";
print "Map Lookup for Location ";
Print ValueOrRun(noun, mapnum);
! print " in area "; Print ValueOrRun(noun, area);
print ":^";
PrintShortName(noun); print "^";
! ObviousExitsSub(noun); Diary of a Text Adventurer has
! a utility to list the obvious exits of a
! room, which I haven't included here.
if (AfterRoutines()==1) rfalse;
! So you can trap MapInfo in the after routine for
! any room and provide additional information.
"There is no further information for this location.";
];
!------------------------------------------------ MapInfoNumber
[ MapInfoNumber a n v h q t; ! A number-parsing routine for
! MapInfoSub's command line.
a = ValueOrRun(location, area);
n = TryNumber(wn);
if (n<=0) rfalse;
! if (n > 0) We have something.
wn++;
v = 1; h = 1;
while ((q==0)&&(v<=MAX_MAP_Y))
{
t = MapLoc(a, h, v);
! the following line was incorrect:
! if ((ValueOrRun(t, mapnum)==n)&&(t has visited)) q = t;
! Because it runs every MapNum routine on the map.
! Instead:
if ((t>selfobj) ! else t isn't a reasonable object
&&(t.#mapnum==2) ! else it's not a value or routine
&&((t.&mapnum)-->0>0) ! else it's likely a routine
&&((t.&mapnum)-->0<100) ! else it's likely a routine
&&((t.&mapnum)-->0==n) ! room object we want
&&(t has visited)) q = t; ! only on map if visited.
#IFDEF DEBUG;
DefArt(t);
print ":^ValueOrRun(object,mapnum): ";
print ValueOrRun(t, mapnum);
print "^object.#mapnum: ";
print t.#mapnum;
print "^(object.&mapnum)-->0: ";
print (t.&mapnum)-->0;
#ENDIF;
h++;
if (h>MAX_MAP_X) { h = 1; v++; }
}
return q;
];
!------------------------------------------------ MapScope
object automapfeature "map"
with name "map" "automap",
number 0; ! used instead of a global to hold
! the direction requested of MapDir
! routines.
[ MapScope; PlaceInScope(automapfeature); ];
!------------------------------------------------ grammar lines
include "grammar";
! You may not want this to be meta in your game --
! That's a debatable matter.
! I've made it meta here because that's how it's
! going to by in Diary of a Text Adventurer, the
! game for which I wrote the mapping system.
! If your verb isn't meta, remove the call to
! BeforeRoutines from MapInfoSub, above.
Extend "examine"
* scope=MapScope -> AutoMap;
Extend "look"
* "at" scope=MapScope -> AutoMap;
verb meta "map" "automap" "whereami"
* -> AutoMap
* "info" MapInfoNumber -> MapInfo
* MapInfoNumber -> MapInfo;
verb meta "help" "about" "hint" "hints"
* -> About;
end;