! ----------------------------------------------------------------------------- ! Inform for New Writers ! ! Mung ! ! A sample game/tutorial for the "Inform for New Writers" guide ! ! Last Modified: Ethan Dicks - 04-Mar-1998 ! ! ! This example showcases how to program events that can interfere with the ! Player's ability to move from room to room down normal passages. Inform ! provides support for restricting access to specific rooms through objects ! programmed to be doors, but no support for the more general case. ! ! The original inspiration for these techniques came from my attempt to port ! Dungeon (the original Zork) to Inform. In Dungeon, rooms can be made ! inaccessible due to explosions, poison gas clouds and such. In addition, ! certain rooms, like the starting room of the Royal Puzzle, implemented a ! "go_in" property to catch certain situations before allowing the Player ! to enter (like setting the initial conditions of the puzzle). The Inform ! libraries do not support programming a routine to be run before permitting ! entrance to a room, and especially do not provide a way to bar entrance, ! once the parser figures out that the Player asked to enter. Here, then, ! are a couple of techniques to permit this level of flexibility. Note that ! it is possible to implement the "mung" functionality with the go_in ! mechanism, but mung doesn't require that the munged room be created with ! munging in mind. go_in would be appropriate to use when a _particular_ ! room needed to be barred for some reason, especially if that restriction ! were temporary or conditional. ! ! To-do: write an ending. ! ! ----------------------------------------------------------------------------- Constant Story "Mung"; Constant Headline "^Inform for New Writers^ ~Mung~ - an Interactive Demolition by Ethan Dicks^"; Release 3; Serial "980304"; ! The game is meant to be experimented with, so we always define: Constant DEBUG; Property go_in; ! An optional subroutine to execute everytime ! a particular room gets entered (can even ! bar Player from entering). Attribute rmungbit; ! Signify that the room cannot be entered. ! -------------------------------------------------------------------------- ! Put your "replaces" here to re-write parts of the Inform libraries Replace GoSub; Replace PlayerTo; Include "Parser"; !------------------------------------------------------------------------------ ! ! First, we need a way to mung the room. To make it easy, there is a special ! Attribute bit to indicate that the room is not enterable from any direction, ! the "rmungbit" (named after the corresponding attribute in the MDL version ! of Dungeon). Next, it's nice to tell Player why the room is off limits. ! When some event occurs that blocks off an area, the code there needs to call ! MUNG_ROOM() with a room number and a text message. Because we override the ! description property of the munged room, it is an irrevokable procedure. If ! we wanted to make it reversible, a slightly different techique (involving ! additional properties and more Inform code) would be appropriate. ! MUNG_ROOM() - destroy the room and remember why [ MUNG_ROOM room message; give room rmungbit; room.description=message; ]; !------------------------------------------------------------------------------ ! ! In order to make a room non-enterable, we have to re-write a couple of ! Library subroutines. These are out of the standard Inform Library ! distribution, version 6.13. The changes are marked off and commented ! below. ! ! -------------------- ! PlayerTo() - from Inform 6.13 with changes by Ethan Dicks [ PlayerTo newplace flag; ! ---- new section ---- ! First see if we _can_ go there at all. if (newplace has rmungbit) { ! Nope. Tell Player why and return false (don't enter) print (string) newplace.description, "^"; rfalse; } ! Now, see if there's something important to do first that might keep us out. if (newplace provides go_in) { ! print "Activating ", newplace, ".go_in()^"; ! Debugging statement if (newplace.go_in()==false) rfalse; } ! ---- end new section ---- move player to newplace; while (parent(newplace)~=0) newplace=parent(newplace); location=newplace; real_location=location; AdjustLight(1); if (flag==0) ; if (flag==1) { NoteArrival(); ScoreArrival(); } if (flag==2) LookSub(1); ]; ! ---------------------- ! GoSub() - from Inform 6.13 with changes by Ethan Dicks [ GoSub i j k df movewith thedir; movewith=0; i=parent(player); if ((location~=thedark && i~=location) || (location==thedark && i~=real_location)) { j=location; if (location==thedark) location=real_location; k=RunRoutines(i,before); if (k~=3) location=j; if (k==1) { movewith=i; i=parent(i); jump gotroom; } if (k==0) L__M(##Go,1,i); rtrue; } .gotroom; thedir=noun.door_dir; if (ZRegion(thedir)==2) thedir=RunRoutines(noun,door_dir); j=i.thedir; k=ZRegion(j); if (k==3) { print (string) j; new_line; rfalse; } if (k==2) { j=RunRoutines(i,thedir); if (j==1) rtrue; } if (k==0 || j==0) { if (i.cant_go ~= 0) PrintOrRun(i, cant_go); rfalse; } if (j has door) { if (j has concealed) return L__M(##Go,2); if (j hasnt open) { if (noun==u_obj) return L__M(##Go,3,j); if (noun==d_obj) return L__M(##Go,4,j); return L__M(##Go,5,j); } if (ZRegion(j.door_to)==2) j=RunRoutines(j,door_to); else { if (j.door_to == 0) return L__M(##Go,6,j); j=j.door_to; } if (j==1) rtrue; } ! ---- new section ---- ! First see if we _can_ go there at all. if (j has rmungbit) { ! Nope. Tell Player why and return false (don't enter) print (string) j.description, "^"; rfalse; } ! Now, see if there's something important to do first that might keep us out. if (j provides go_in) { ! print "Activating ", j, ".go_in()^"; ! Debugging statement if (j.go_in()==false) rfalse; } ! ---- end new section ---- if (movewith==0) move player to j; else move movewith to j; df=OffersLight(j); if (df~=0) { location=j; lightflag=1; } else { if (location==thedark) { DarkToDark(); if (deadflag~=0) rtrue; } real_location=j; location=thedark; lightflag=0; } if (AfterRoutines()==1) rtrue; if (keep_silent==1) rtrue; LookSub(1); ]; ! ---------------------------------------- ! ! Now include the standard library definitions Include "VerbLib"; ! -------------------------------------------------------------------------- ! ! It all starts here --+ ! | ! v [ Initialise; location = SHED; "You can't make an omlette without breaking some eggs. Likewise, you can't work in a mine without blowing stuff up. What a job!^^"; ]; ! -------------------------------------------------------------------------- ! ! Things - the elements of our interactive world ! ! -------------------------------------------------------------------------- !---- ! First, a place to find the explosives Object SHED "Storage Shed" with description "This is the explosives storage shed for the BLAMMO Mining Company. There is barely enough room for you to turn around without knocking over a crate of dynamite.", out_to MINE1, n_to MINE1, has light; !---- !Next, a handy container Object -> CRATE "open crate" with name "crate" "box", description "One crate lies open at your feet. On the side it says: ^ 1. Pull fuse.^ 2. Drop dynamite.^ 3. Run away.", capacity 8, ! If we make this 0, Player can't put stuff back in box has open container static; !---- ! Now, to make the dynamite Class Dynamite with name "stick" "bomb" "saftety" "dynamite" "fuse" "label", description ! Let's make the description interesting. [; print "It's an stick of safety dynamite with a three second, self-igniting fuse already attached"; if (self has general) print" (which is burning)"; print_ret ". The directions read: ^^ ~Pull To Light. ^ (Do Not Eat)~"; ], short_name "stick of dynamite", plural "sticks of dynamite", Before [; Eat: "Don't people read labels anymore?"; Pull: if (self has general) ! It can't be lit twice! "It's already lit."; else { give self general; StartTimer(self, 3); "The fuse lights."; } ], time_out [; ! Don't let the dynamite kill the Player... Death is a ! whole 'nother example. ! ! Note: we neatly sidestep the issue of containers by ! not allowing the dynamite to be contained by anything ! except a room or the Player object. In a real game, ! determining what to mung might be more complex (or, if ! Player death is allowed, it's easy - use "location" as ! the room to mung, then kill the player. Of course, it ! only makes sense if you then also support resurrection. ! See if Player is holding the dynamite if (self in player) { remove self; "Fortunately for you, this is safety dynamite. It detects your presence and goes off in your hands with little more than a shrill ~POP~."; } ! See if Player is close to the dynamite if (TestScope(self, player)) { remove self; "Had this been ordinary dynamite, you would now be blown to smithereens. Since it was safety dynamite and you were within the normal blast range, it detonates with merely a brief flash and a loud ~BANG~."; } ! OK. All clear. It's safe to blow up. MUNG_ROOM(parent(self), "Much as you would like to enter the area, the roof has collapsed due to the careless application of high explosives."); remove self; "^^ BOOOOOM!!!!^"; ], time_left 0, ! The daemon stores the number of turns left here has ~static; ! Of course you can pick up the dynamite ! Now, fill the crate Dynamite -> -> ; Dynamite -> -> ; Dynamite -> -> ; Dynamite -> -> ; Dynamite -> -> ; Dynamite -> -> ; Dynamite -> -> ; Dynamite -> -> ; !---- Object -> NOTE "announcement" with initial "There is a piece of paper taped to the door here.", inven "A note from the foreman.", name "note" "paper" "piece" "announcement", before [; Examine: "This is a standard safety warning from the foreman, telling the blasters to be careful when placing charges in the mine because the roof is unstable."; ], has ~static; ! make sure it's portable !---- !Finally, a useful object to illustrate the go_in property Object -> MASK "rebreathing apparatus" with name "mask" "rebreathing" "apparatus" "rebreather", description "It is a small metal canister sporting a mouthpiece at the end of a rubber hose. It is used to provide fresh air in areas that are unsafe.", ! Let's say something cute when Player picks this thing up. after [; Take: "You pick up the rebreather and hang it on your belt."; ], has ~static; ! Make sure Player can cart it around. ! ---------------------------------------- ! ! Out and about the mine. There's lots of rooms to give lots of places ! to blow up. Take special note of the ring-crawl to the north of the ! shed. When you blow up one room, access is blocked from all sides. ! The "mung" technique avoids the tedious programming chore of coding ! all the entrances to a room and the nearby exits that lead to them. ! !---- Object MINE1 "Antechamber of BLAMMO Mine #6" with description "This is the largest room in the mine, but that's not saying much. You can nearly scrape the roof with your safety helmet when you stand up straight enough. The storage shed is just south of you. Tunnels lead off in several directions. There is a sign posted by the southeast tunnel.", s_to SHED, se_to SE_TUNNEL, e_to E_TUNNEL, w_to W_TUNNEL, nw_to NW_TUNNEL, ne_to NE_TUNNEL, has light; !---- ! Warn Player that the upcoming area is unusual. Object -> SIGN "caution sign" with name "sign" "warning" "caution", before [; Examine: "^^ CAUTION: ASPHYXIATION HAZARD ^ Do not enter this area ^ without proper safety equipment."; ], has scenery static; !---- Object SE_TUNNEL "Abandoned Tunnel" with description "This part of the mine has been disused for years. The air is heavy with coal gas. Anything of interest has long ago been carted off.", ! Through the "go_in" property, we make one of two consequences for entering ! this room - if Player lacks the rebreather, print a message and block ! the entrance (a less friendly game could kill Player here); if Player ! has the rebreather, print a message acknowledging the fact and allow ! free passage. The difference is what go_in returns: rfalse to block, ! rtrue to admit. go_in [; if (MASK notin player) { print "Lacking the proper equipment to breathe safely in there, you stop just short of the mouth of the tunnel.^"; rfalse; } "Before entering, you take a moment to use the rebreather."; ], nw_to [; print "As you emerge into the fresh air, you remove the rebreather from your mouth and hang it back on your belt.^"; return MINE1; ], has light; !---- Object E_TUNNEL "East Tunnel, BLAMMO Mine #6" with description "This tunnel is like the others, too short to stand up in, with support beams every few feet and a string of electric lights leading off into the gloom in both directions. Going East will take you deeper into the mine.", e_to E_WORK, w_to MINE1, has light; !---- Object E_WORK "East Work Face" with description "The tunnel ends here in a blank rock wall. The only way out is back the way you came.", w_to E_TUNNEL, has light; !---- Object W_TUNNEL "West Tunnel, BLAMMO Mine #6" with description "This tunnel is like the others, too short to stand up in, with support beams every few feet and a string of electric lights leading off into the gloom in both directions. Going West will take you deeper into the mine.", w_to W_WORK, e_to MINE1, has light; !---- Object W_WORK "West Work Face" with description "The tunnel ends here in a blank rock wall. The only way out is back the way you came.", e_to W_TUNNEL, has light; !---- Object NW_TUNNEL "Northwest Passage" with description "This passage is little more than a crawl. You can't see around the curve to the north, but you can see the storage shed to the southeast.", se_to MINE1, n_to N_TUNNEL, has light; !---- Object NE_TUNNEL "Northeast Passage" with description "This passage is little more than a crawl. You can't see around the curve to the north, but you can see the storage shed to the southwest.", sw_to MINE1, n_to N_TUNNEL, has light; !---- Object N_TUNNEL "North Corridor" with description "The crawl opens up to a full-fledged east-west corridor. The rock here looks freshly blasted and very unstable.", se_to NE_TUNNEL, sw_to NW_TUNNEL, e_to N_WORK1, w_to N_WORK2, has light; !---- Object N_WORK1 "Northeast Work Face" with description "The corridor comes to an abrupt end in a shear face. The way out is to the west.", w_to N_TUNNEL, has light; !---- Object N_WORK2 "Northwest Work Face" with description "This is the end of the line. The mine is east of here.", e_to N_TUNNEL, has light; !---- ! Define verbs here... Include "Grammar"; ! We want to make sure that Player is holding the dynamite before pulling ! the fuse - otherwise we have to code what happens if a stick of safety ! dynamite goes off in a box (possibly next to other sticks of dynamite). ! (Of course, Player _can_ just light a stick and put it back in the box, ! but that's not a problem to worry about here). Extend 'pull' replace * held -> Pull;