! 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 jonadab@bright.net ! 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;