WelcomeWhat's NewsInform GuideORLibraryDownloadsLinksWebMaster@OnyxRing.com

This site
produced
656719
pageviews since
8/19/2004

Today's date:
12/19/2018


Author: Inform Moderator
Title: Part C2 : Miscellaneous ORLibrary Modules (part 2)

Creation Date: 8/9/2004 6:11:46 PM
Last Updated: 8/9/2004 6:11:46 PM


NOTE: This article has been depreciated along with all of the original ORLib Manual efforts. The newer manual can be found at http://www.onyxring.com/ORLibDocToc.aspx




Part C2 : Miscellaneous ORLibrary Modules (part two)

This part is the logical continuation of section C. In order to avoid repetition of material already covered, let's expand upon the game we have already been building and fill create the lower level of the house. No new library entries need be covered for this so we'll just get straight to the point:

 
 MyRoom Hall "Hall" 
   with w_to foyer 
   , e_to staircase 
   , descrip "This was a long hallway connecting the 
          western entryway with the small landing at 
          the base of the staircase to the east. " 
 ; 
 MyRoom staircase "Staircase" 
   with w_to hall 
   , n_to library 
   , descrip "I was at the base of a magnificent marble 
          staircase leading upwards. A guard stood here 
          blocking travel up the stairs to all the suspects. 
          Up these stairs was where I would go when I had 
          finally solved this case. Alternately, exits lay 
          north and west. " 
   , firsttimepost "I knew that the police commissioner 
          was up these stairs, waiting to congratulate the 
          investigator who unraveled this puzzle first. It 
          was my intention to be that person." 
 ; 
 MyRoom livingroom "Living Room" 
   with s_to foyer 
   , n_to diningroom 
   , descrip "The living room was a comfortable sitting 
          area. It was well decorated, but lacked some of 
          the luster and elegance found throughout the rest 
          of the house. To the south I could make out the 
          foyer, and to the north a dining area. " 
 ; 
 MyRoom diningroom "Dining Room" 
   with e_to kitchen 
   , s_to livingroom 
   , descrip "The room around me was strangely bizarre in 
          its decor. It was elegant, true to the rest of the 
          house, and as large as any dining room I had ever 
          been in. Yet the porcelain tiling and glass 
          tabletop gave it a cold, sterile quality that I 
          had instinctively come to associate with a 
          breakfast nook. Possibly this impression was 
          reinforced by the presence of the walk-through 
          kitchen to the east. To the south I could see a 
          much more comfortable looking room." 
 ; 
 MyRoom kitchen "Kitchen" 
   with w_to diningroom 
   , e_to sunroom 
   , descrip "The kitchen clean and well kept. It was 
          brightly lit by the glass room that I could see to 
          the east and cast light to the dining area to the 
          west. " 
 ; 

Additionally, we'll need to slightly modify the Library, Foyer, and Sun Room to complete the map and tie all the rooms together:

 
 MyRoom library "Library" 
   with w_to study 
   , s_to staircase !---added line 

 ... 

 MyRoom SunRoom "Sun Room" 
   with s_to study 
   , w_to kitchen !---added line 

 ... 

 MyRoom foyer "Foyer" 
   with n_to livingroom !---added line 
   , e_to hall !---added line 

 ... 

And as a final change, we can move the player’s starting position back to the foyer were it was previously:

 
 Location=Foyer; 

11. ORProp

This class eases implementation of a generic object which does not need to be referenced in the game. Sample use:

 
 ORProp -> "butterflies" has pluralname with name 'butterflies'; 

This method has two distinct advantages over specifying words in the room's name property. Specifically, it allows the developer to specify the pluralname attribute thereby generating a plurality sensitive message such as "those don't need to be referenced in this game" rather than the standard singular version "that doesn't need to be ..."

The second advantage is that it works in conjunction with packages that utilize the name property for rooms (Such as ORLookRoom).

To continue our tutorial, we will add some of the items mentioned in room descriptions, but of no real use:

 
 ORProp "shards of glass" laboratory has pluralname; 
 ORProp "woodworking" study; 
 ORProp "carpeting" study with name 'carpet'; 
 ORProp "porcelain tiling" with name 'tile' diningroom; 

12. ORRandom
This module is designed to compensate for a bug in the WinFrotz interpreter involving random number generation. See the article "Randomizing Random" in the "Informed Tips" section of An Inform Developer's Guide at www.OnyxRing.com for a description of this.

Additionally, this module exposes the routine ORInitRandom() to house code that would normally be placed in Initialise(), but needs to use an adequately seeded random number generator. ORInitRandom is called immediately following the first user input.

There is no required code to make this module work alone, but use of the ORInitRandom routine can be seen in the section on ORGibberish.

13. ORGibberish
Some modules are fun, but have little practical use. Still, working under the belief that the existence of such a module creates its own need, we've introduced ORGibberish, which generates random, human pronounceable words of varied length.

The ORGibberish object defines a method called MakeWord(). MakeWord() takes a single number as a parameter which specifies the number of requested vowel sounds. It then generates a random word, with the requested number of vowels, based upon rules derived from phonetic principals of pronunciation and then prints it.

Of what possible use could this module be? Random passwords, the names of monsters, and spell incantations come to mind.

It is important to say here that most entries in the ORLibrary do not create objects, but rather define classes for the developer to create. There are a few notable exceptions of which ORGibberish is one and ORNPC is another.

Use of the ORGibberish object is basic. Although it can be leveraged in many more powerful ways, as we will see in the ORDynaString example, for our tutorial we will exemplify it using the ORInitRandom routine (see ORRandom for more information on the ORInitRandom routine):

 
 [ORInitRandom; 
     ORGibberish.MakeWord(2); 
     print "^"; 
 ]; 

Now run the program. Pressing return after the first prompt will fire the ORInitRandom routine. The output will likely not be even remotely similar to this:

 
 squeesle 


This code is given purely for example, and will not be included in the tutorial we have been working on. Another example of using the ORGibberish object, which will appear in the final tutorial, appears in the ORDynaString section.

14. ORDynaString

There is little need for dynamic string generation in Inform. Hundreds of games have been written without the dynamic strings ever being needed or even desired. Still, there are a very few occasions where building a string dynamically is more elegant than alternative methods. (See the ORInsultCompli_KT entry for one such example of this.)

ORDynaString is a wrapper around the @output_stream functionality. That is, it is turned on to print text to an array, and then turned off. The resulting string, stored in an array pointed to by the "buf" property (by default, ORDynaBuf) can then be printed using the (dynastring) print rule. For interchangeability, the (dynastring) print rule also works equally well with strings.

The following routines are exposed from the ORDynaString class:

  • buf - points to an array which will store the target string.
  • capture() - redirect all printed text to the array pointed to by buf.
  • release() - stop redirection text to array and redirect back to the screen.
  • upper(id) - takes the character stored in the array at position id (1-based index) and makes it uppercase.
  • lower (id) - takes the character stored in the array at position id and makes it lowercase.
  • upper_all() - makes the entire string upper case.
  • lower_all() - makes the entire string lower case.
  • get_char(id) - returns the character at position id.
  • set_char(id,val) - sets the character at position id to the value val.
  • length() - returns the length of the string.

As was already stated, the need for dynamic strings seldom arises, but for example purposes, let us create an object that would be slightly more difficult to create without them. To be specific, an office desk toy that displays a different word/definition every time it is shaken. For amusement, we'll leverage the ORGibberish entry to generate random, human-pronounceable words and piece together a random definition to accompany them. We will also leverage the read_value property defined in the ORDistinctRead module. Take note of the implemented glass table mentioned in the description of the dining room upon which the cube will be initially placed.

 
 MyObject glasstable "glass table" diningroom 
   has supporter scenery 
   with descrip "The table was glass. Clean and transparent 
          the table showed not a fingerprint. " 
   , firsttimepost "It was probably from an expensive 
          line of furniture, but as I looked at it, I could 
          not help but be reminded of a cheap glass table 
          from my own apartment. The two were nearly identical, 
          except mine was much smaller and had several scratches." 
 ; 

 ORDynaString rword; 
 ORDynaString rtext; 
 array word_buf -> 30; 

 MyObject word_cube "dictionary cube" glasstable 
   with name 'word' 'definition' 'toy' 'office' 'electric' 
   , descrip "It was an electric office-toy in the shape 
          of a cube. It displayed a changing dictionary 
          definition of little-known words which could be 
          read at any time." 
   , before[; 
          Shake: self.Shuffle(); 
               print (ig)CIVerb(actor,"shook","shake")," the 
               cube and the words ", (ig)ppf("rearranged", 
               "rearrange")," themselves."; 
               rtrue; 
   ] 
   , Shuffle[; 
          give self general;  !—--we have shuffled at least once 
          rword.buf=word_buf; !---assign different array 
                              !---so dynas don't overwrite 
                              !---each other 
          rword.capture(); !—--start capturing word text 
          ORGibberish.MakeWord(2); !---make gibberish 
          rword.release(); !---done with new word. 
          rtext.capture(); !---now capture formatted text 
          print "On the cube appeared the following 
                    word/definition pair:^^"; 
          rword.upper(1); !—capitalize the word 
          print"~",(dynastring)rword," - "; 
          rword.lower (1); !—uncapitolize 
          !---generate and print a random definition... 
          noun=random(true,false); 
          if(noun) print "(N) The"; else print "(V) To"; 
          print " ",(dynastring)rword," is "; 
          if(noun) print "a"; else print "to"; 
          if(noun){ 
               print (string)random("n abysmal" 
                    ,"n abominable"," brainless"," dreadful" 
                    ," horrendous"," miserable"," stupid" 
                    ," fabulous"," marvelous","n amazing" 
                    ," popular"," delightful" 
                    , " valuable")," "; 
               print (string)random("barrel of" 
                    ,"apology for","pile of","heap of" 
                    ,"sack of","glob of","paragon of" 
                    ,"modal of","example of", "hunk of" 
                    , "bundle of","manifestation of")," "; 
               print (string)random("monkey filth" 
                    ,"vomit","gunk","garbage","fertilizer" 
                    ,"lizard snot","disirablity" ,"precision"
                    ,"accomplishment","pig phlegm"
                    ,"maggot fodder"); 
          } 
          else{ 
               print " "; 
               print (string)random("cry to" 
                    ,"yell at","smell","examine closely" 
                    ,"mix up","spit upon","pee against" 
                    ,"insult","rub down","worship","flatter" 
                    ,"propose to","forget about")," a"; 
               print (string)random(" stranded" 
                    ," dirty"," neighbor's favorite" 
                    ," free"," smelly"," disproportionate" 
                    ," slightly used","n insignificant" 
                    ,"n extrordianary"," stolen" 
                    ," recently washed"," spoiled")," "; 
               print(string)random("daughter","idol" 
                    ,"donkey","tree","mailbox","monkey" 
                    ,"wife","brother","lunch","coat" 
                    ,"lawyer"); 
          } 
          print ".~^^Followed by the words ~Shake cube to change 
               definition.~"; 
          rtext.release(); !—--done capturing text 
   ] 
   , read_value [; 
          if(self hasnt general) self.shuffle(); 
          print(dynastring)rtext; 
   ] 
 ; 

Now all that remains is to actually define the verb shake and provide a default message:

 
 Verb "shake" * held -> Shake; 
 [ShakeSub;print (ig)CIVerb(actor,"shook","shake")," ",(the)noun, 
   ", and ",(ig)vrb(actor,"felt","feel")," better, less tense.";]; 


15. ORDipensor

This is an object that is designed to dispense seemingly endless quantities of a specific object. It is useful for things like a pot of gold coins, where the pot may contain numerous gold coins, but the developer does not want to actually create all coins at once. The class of the items that the derived object dispenses must be defined as the property "itemclass". When the Dispensor object is initialised (automatically), a number of objects of the itemclass type are created. This number is stored in the "initial_count" property. When the items are removed, more are generated to replace them; when the items are returned to the dispensor, they are destroyed.

The following properties are of particular interest when dealing with the ORDispensor class:

  • itemclass - Set this property to the class of the array that will be dispensed.
  • conceal_dispensing_items - Set this to true if the dispensing items will have the concealed attribute while they are in the object. This is particularly useful when its dispensing item defines the object represented by this class. For example, a pond is only a pond by virtual of the fact that it has water in it. In this case, a description of a pond full of water should show it as empty despite being filled with water, so this property would be set to true.
  • accept_alternate_itmes – This property is set to disallow insertion of items that are not of the same class as the itemclass.
  • Initial_count – Set this to the number of instances of itemclass that should initially be visible.
  • receive_item_msg – outputs the message when an item is inserted into the object.
  • cannot_receive_item_msg – outputs the message when an item cannot be inserted into the object.

For our tutorial, let us create a candy dish:

 
 MyObject coffeetable "coffee table" livingroom 
   has supporter scenery 
   with name 'wood' 'wooden' 
   , descrip "The coffee table was made of wood and was 
          slightly worn. " 
 ; 
 class candy(40) 
   class ORFirstThoughts 
   has edible 
   with short_name "piece of dry candy" 
   , name 'candies//p' 'peppermint' 
   , descrip "The candy was an unwrapped peppermint. " 
   , plural "dry candies" 
   , plural_many "several" 
   , before[; 
          eat: candy.destroy(self); 
          print (ig)CIVerb(actor,"ate","eat"), 
               " ",(the)self,". "; 
          if(actor==player) print "Very good, I 
               thought to myself. "; 
          rtrue; 
   ] 
 ; 
 ORDispensor CandyDish "dish" coffeetable 
   class ORFirstThoughts 
   has open 
   with name 'crystal' 
   , itemclass candy 
   , accept_alternate_items false 
   , initial_count 10 
   , descrip "The dish was made of crystal. " 
 ; 

16. ORReferByContents

The ORReferByContents object provides a specialized parse_name routine that allows a container to referred to by the objects it contains. For instance, when given a jar of marbles, it is perfectly natural for the player to try to “PUT THE MARBLES ON THE TABLE” when “PUT JAR ON TABLE” is what was intended. This is even more likely for other types of contained substances, such as a bottle of water.

By default, any object that inherits from the ORReferByContents class will be considered by the parser for any verb. This works well for verbs like Drop, Take or Throw. There are also several actions where considering the container may not be the appropriate solution, such as Eat. To cause the container to not inherit its container's names when certain verbs are used, the "ignore_actions" property list is provided.

Alternatively, for developers may find that more verbs are disqualified than are selected. In these cases, the "refer_actions" property list is provided.

For our tutorial, we will create a glass bottle which things can be placed in. Note that the ORReferByContents does not automatically imply the container attribute since it could just as easily have been a tray.

 
 MyObject glassbottle "glass bottle" coffeetable 
   class ORReferByContents 
   has transparent open container 
   with descrip "It was just a glass bottle, capable of 
          containing all kinds of things." 
   , ignore_actions ##Eat ##Drink 
 ; 


17. ORLiquid

Liquids have been touted as being some of the most troublesome objects to implement. This is primarily because they do not act as regular objects. They follow different rules. Water cannot be held in same manner as a book, so rules like take and drop do not apply to liquids. To put liquid on the floor is to have seep into the carpet and effectively disappear. Different verbs apply to liquids such as pour. A quantity of liquid can almost always be divided in half. There are also non-liquid substances that follow some of the same basic rules as liquid, such as sand.

Perhaps one of the most troublesome features of a liquid substance is the fact that they are always treated neither as singular nor plural. You do not say that you have "a water" as you would have "a coin." Neither do you say that the "water are in the bowl" as you would say that the "coins are." For non liquid substances that can be handled in collections (like coins), there is both a plural and a singular form (e.g. "coin" and "coins"). This is not the case with liquid like substances. Although there may be several water objects defined in a game, to refer to each object alone requires a form of measurement that plurality and singularity can be applied to, such as "ounce(s)" or "handful(s)" or "measure(s)".

The ORLiquid module was designed to ease the difficulty in implementing liquids in a game. Two classes are defined in the module: ORLiquid and ORLiquidSource which is an implementation of ORDispensor. Additionally, several liquid specific verbs have been defined, like scoop and pour and a few others have been extended.

Two routines have been created to determine if an object can contain liquids of various types: CanContainDryLiquid(), and CanContainWetLiquid().

Also some attributes have been created to help define the world model:

  • wetliquid - to be applied to ORLiquid objects if they are to indeed act as liquid as opposed to the so called dry liquids, such as sand or dirt.
  • wet - given to objects that have been dipped in wet liquids.
  • water_tight – given to containers so signify that they can, indeed contain wet liquids.

Additionally, because liquids can not be poured into just any container, a property called "liquid_measures" must be provided by any container that can contain liquid. This property defines what quantity, or how many measures, of liquid can be contained in the object. Note that this property can also be defined for other objects that are not containers as well. These objects, when placed within a liquid carrying container, reduce the amount of space available for liquid. For example, a glass with a liquid_measures property of five, that contains three ice cubes, each with a liquid_measures of one, will have room for two measures of liquid.

For our tutorial, let's take the bottle that we created and make it capable of holding five measures of water:

 
 MyObject glassbottle "glass bottle" coffeetable 
   class ORReferByContents 
   has transparent open container water_tight 
   with descrip "It was just a glass bottle, capable of 
          containing all kinds of things." 
   , ignore_actions ##Eat ##Drink 
   , liquid_measures 5 
 ; 

Now we can define two liquid substances, one dry, and the other wet:

 
 class sand(20) 
   class ORLiquid 
   with short_name "sand" 
   , plural "handfuls of sand" 
 ; 
 class water(20) 
   class ORLiquid 
   has wetliquid 
   with short_name "water" 
   , plural "ounces of water" 
 ; 

The sand and water will be dispensed by the beach and the ocean respectively. These two are placed in the most reasonable location, the beach:
 
 ORLiquidSource beachsand "beach" beach 
   has supporter ~container 
   with itemclass sand 
   , conceal_dispensing_items true 
 ; 
 ORLiquidSource ocean "ocean" beach 
   with itemclass water 
   , conceal_dispensing_items true 
 ; 

As an added touch we can create a before rule to the beach location which will translate drop actions into puton actons, so that things dropped end up in the sand. Here is the beach location again in its entirety:

 
 MyRoom Beach "Beach" 
   with s_to darkdoor 
   , w_to glassdoor 
   , e_to "No, I didn’t have time to go for a swim." 
   , n_to "The trees blocked completely any travel in that 
          direction. No clues would be found there." 
   , remote_description "Through the door I could see a 
          beautiful beach with water lapping up on the ocean 
          shore line." 
   , descrip "Beautiful and peaceful. My shoes were half 
          buried in the sand. Water lapped continuously upon 
          the shore merely an arm's reach away. " 
   , firsttime "The scenery rendered my thoughts of 
          house murder all but forgotten. " 
   , before[; 
   drop: <<PutOn noun beachsand>>; 
 ] 
 ; 


Note how the beachsand object and the beach room share the same name. Since one is a room, and is only referenced by the specialized scope rules defined in the ORLookRoom module, these will not conflict.

18. ORDefer2ndReference

The tutorial as it stands acts can act a little unpredictably. As it stands we have several objects that inherit from the ORReferByContents object. To be specific, both liquid sources and the glass bottle inherit from this. This allows for some strange command configurations that confuse the parser, but still make sense to people. For example, fill the glass bottle with sand and then type the command (from the beach) "Pour bottle into sand." This will result in the parser believing that the word SAND refers to the bottle. Sure, we could add ##Pour to our bottle's "ignore_actions" property, but then that would prevent us from being able to type "Pour sand into water." What we really need is a way of skipping consideration of the bottle if it was already the first noun. This is done via the ORDefer2ndReference module. In effect, when the parser is choosing an object for the variable "second," it will defer to a different object rather that encourage the same object to be referred to twice.

No code is needed for this module. Just include it for our tutorial pour sand into sand all day long.

19. ORNumberedContainer

This is an object that is designed to emulate a collection of openable, stationary containers (like lockers).

The following properties are of particular interest:

  • singular_name – the name of a singular compartment (e.g.
    "locker").
  • start_numb – the minimum number assigned to the compartments.
  • end_numb – the maximum number assigned to the compartments.
  • numbered_description – this routine will print pertinent information about which compartments are open and what contents are available. It is useful to call this routine after the description.
  • contained_obj & contained_in - these property lists hold the contents of each locker. There should be enough elements in these arrays to hold all potential contents. Both of these arrays should be the same size as there is a one to one relationship between the items in these lists.
  • open_state - a list of the compartments that are currently open. If more compartments are provide than there is room in this list, then the potential exists for the open to fail. In this case the property "cannot_open_more_msg" is run which outputs an appropriate message.
  • cannot_open_more_msg – output when the player has tried to open more compartments than there is room for.
  • add_item_to – call to add an object to a specific container.
  • remove_item – call to remove an object.

As a note, the display name, or short_name as may be the case, is displayed when referencing the object as a whole and should be plural in form (e.g. "lockers"). The singular_name property is used when referring to a single construct so should be singular in form.

Also, although it is fine to redefine the array properties of this object, it might prove to be easier to define an array table and assign it upon initialization.

For instance:

 
 array lockerstate table 1000; 

 [Initialise; 
   numcont.open_state=lockerstate; !—--allow 1000 lockers 
                                   !---to be open at once. 
 ];

For our tutorial, lets create this as an eccentric object in the hall. First, we'll add a description of it to the hall:

 
 MyRoom Hall "Hall" 
   with w_to foyer 
   , e_to staircase 
   , firsttime "A flag when off in my head as I stepped 
          into the hall. Something was strange here... 
          but what? I considered it for a moment before 
          it hit me. The tiling on the southern wall was 
          something other than it first appeared. In fact, 
          as I looked closely, each tile appeared to be 
          a door to a small cubby hole. Like little 
          mailboxes... ^^" 
   , descrip "This was a long hallway connecting the 
          western entryway with the small landing at the 
          base of the staircase to the east. The southern 
          wall was lined with numerous tiny cubby doors, 
          hidden in the tiling. " 
 ; 

Now we can actually create the object in the hall:

 
 MyObject tilecubby "tile cubbies" hall 
   class ORNumberedContainer 
   with name 'cubby' 'door' 'doors' 
   , singular_name "cubby" 
   , end_numb 9999 
   , descrip[; 
          print "Almost invisible, the tiles along the southern 
               wall were actually tiny doors to little cubby 
               spaces with numbers etched into them. There were 
               thousands. "; 
          self.numbered_description(); 
          print " "; 
   ] 
   , firsttimepost [; 
          print "I looked at the tiled cubbies 
               in wonder. ",(italics)"What kind of eccentric 
               people would do something like this? ";
   ] 
 ; 

For a final bit of fun, let's create a revolver...

 
 Global gun_position; 
 MyObject gun "revolver" 
   with name 'gun' 
   , descrip "The gun was sleek and heavy; powerful and deadly. " 
   , firsttimepost "I fell immediately in love with the gun, 
          having a liking for that sort of thing. " 
 ; 

...and hide it in a random cubby. The initialise() routine will serve us well for that:

 
 gun_position=random(tilecubby.end_numb); 
 tilecubby.add_item_to(gun,gun_position); 



Table of Contents:
Table of Contents
Previous:
Part C1: Miscellaneous ORLibrary Modules

Next:
Part D : Advanced Library Entries




Copyright 2004 - 2018 : Inform Moderator
OnyxRing.com has been given the potentially non-exclusive right to display the content of this article. However the original author retains all rights. Permission to reproduce this article -- either in part or in whole -- is left strictly to the discretion of the original author.