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 B : Basic ORLibrary
In this section we will begin our ORLibrary tutorial by actually starting development of a murder mystery game called "Whodunnit". First, we'll review the OR_BlankGame template file...
1 Overview of OR_BlankGame
The OR_Blankgame.inf file, as the name implies, is a template file used as a starting point for games leveraging the ORLibrary. The following is a review of various parts of the file.
With the ORLibrary framework, including registered library entries is as simple as defining the appropriate constant. In order to keep from having to lookup the appropriate constant whenever you want to include a library entry, the blank game template comes with all the current constants already defined, but commented out. All that is required to include a library entry from a file based on the OR_Blankgame.inf template is to locate the appropriate constant, and uncomment it. The following demonstrates a game including the ORDoor library entry:
1.3 Commented segments
There are several commented sections used to indicate where certain types of code should go. Note that this is mostly a matter of preference. Feel free to move these about or eliminate them entirely if you so desire. Honoring the convention does have two added benefits, however: It helps to keep your code organized, and it insures code is place appropriately in respect to both the ORLibrary includes and the standard library includes.
1.4 Include locations
The three standard library include files (Parser, Verblib, and Grammar) dictate, by their nature, four sections in any Inform program where blocks of code can be placed. At each of these four locations you will find the following include directive:
This file is the common entry point for all framework entries. It expects to be included exactly four times and keeps track of how many times it has been included. This information is then passed along to the library entries, which may or may not choose to provide code for compilation during that pass. All library entries must be registered with this file in order to be accessible.
1.5 Additional pieces
Additionally, there are a few places in the template where generic every-game code has been created. These include the Serial and Release constants as well as the Initialize routine. These are no different than they would be in a standard, non-ORLibrary game, and are included simply for convenience.
2 Building a Basic Two Room game
We could jump immediately into an overview about the ORLibrary, but it will be much more beneficial if we have a subject for our discussion. Let's begin with a "Hello-World" caliber game using the OR_Blankgame template. Don't bother uncommenting any of the library entries for now. We'll get to those in a moment.
2.1 Beginning Layers
As was stated above, it is a good idea to create "base classes" for our objects to derive from. This is a good idea even if you have no code to put in the base class since it provides a place to implement common expansion in future enhancements. The following code, placed in the "Object Templates" section of the blank game template, will create a base class for objects:
And the same can be done for a base room object:
Notice the definition of the light attribute. This is one (incredibly small) example of the usefulness of base classes. All rooms derived from MyRoom will have the light attribute by default. This is a fairly useful practice, since in the design of a hundred-room game you've saved yourself from having to specify the light attribute 99 times. Even in our very small game we see (some) benefit. Base classes become much more useful as game complexity increases. Keep in mind that if you want to create a dark room that is derived from the MyRoom class you can always unset the light attribute (~light).
2.2 Two Rooms and an Object
Now, using our base classes, we can create two rooms, joined in an east-west fashion and a simple object in one of the rooms.
2.3 Positioning the player
With the last step of placing the player in a room, we are finished with our two-room example. Put the following line in the Initialize function:
Now compile. If you're too lazy to type out the example don't worry about it. You can get this source already imposed on the blank game template here. Let's review the results...
Even a beginning Inform programmer may notice that the previous example isn't any different than a plain non-ORLibrary program. What was the point of the exercise? It was to demonstrate the first major aspect of OREnglish: The ability to create games in different tenses and person.
3.1 The Same...
Other than the use of the game template and the OREnglish file, there was nothing new introduced in the coding of the previous two-room example. So what's different about the z-file produced? Hardly anything. The very same code given above will compile without the ORLibrary. As the following transcript shows, when you run the game you get the same (or similar) default messages, that you would get without the ORLibrary:
The same code produces the same messages with or without the ORLibrary. No surprises there. Still, if we look at the compiler output we see that the same library entries that we discussed in section 3 were pulled in as "automatic dependencies" again. The OREnglish file was compiled in too. Yet these don't appear to have any affect.
This demonstrates the "transparency" striven for when designing ORLibrary entries. That is, unless instructed to do so, these library entries will mimic the behavior of the standard library. Without forcing us to use it, the OREnglsh module and it's dependencies have given us a great deal of flexibility that we do not have with the standard library alone.
3.2 ...Yet different
One of the first things that OREnglish provides us, with the aid of ORPronoun and ORTextFormatting, is the ability to change the person and tense of the narrative. To demonstrate this, add the following command to Initialize() routine:
Now recompile and run. By typing in the same commands as we did previously, we can see that the default messages have indeed changed:
All library messages have been redesigned to adapt to the current Person/Tense settings. At the time of this writing, additional settings for the SetPersonTense routine include SECOND_PERSON (the default), as well as PRESENT_TENSE (also the default). These can be mixed and matched.
The additional settings of THIRD_PERSON and FUTURE_TENSE were considered for a time and have partial support code, but have yet to be fully implemented.
3.3 Creating rudimentary person/tense sensitive Narrative
Of course only the default messages have changed. The room descriptions are still rendered exactly as we typed them, in present tense. Generally this isn't a problem. When a developer designs a game for past tense, the descriptions should reflect this just as they do when a game is designed in present tense. There are few places however, such as a reusable library entry or a game that allows the person/tense to change dynamically, where it is necessary to create text that transforms itself.
3.3.1 ppf() and fst()
The NarativeTense and NarativePerson variables are set by the SetPersonTense() routine. It is possible (although not recommended) to modify your text by checking these values as in the following example:
The same can be done to achieve person sensitivity by checking the NarativePerson variable. This makes for somewhat clumsy code, however. OREnglish provides two rudimentary routines to help streamline the creation of person and tense sensitive narative: ppf (past, present, future) and fst (first, second, third). We'll touch briefly on these two routines, but be forewarned that due to nuances in the English language, they are not as useful as they may first appear. They do, however, provide the basis for a better, more powerful set of routines for creating morphing text, which will be covered in depth in section 5 (ORPronoun).
For now they serve our needs adequately:
Now the descriptions of the objects will be appropriate regardless of the current settings of the game-narrative:
It may be worth noting the use of the (ig) print rule when using the ppf() and fst() routines in a print statement to keep from printing the return value. This, and several other useful print rules are defined in the ORTextFormatting entry, which is covered in section 9.
3.4 Changes made by ChangePlayerTense
In the above example, the reader may have noticed the use of the little-used IS__TX constant. Actually, the ORLibrary has redefined this into a variable and changes it appropriately when ever the ChangePlayerTense() routine is called. The same has been done for the following former-constants:
Additionally, the CANTGO__TX constant is also changed as well as the accompanying default message that it maps to.
3.5 Revamped OREnglish library functions and defines
There are several routines defined in the standard library's English file that have also been modified to be person/tense sensitive. These being:
Also, for the sake of completeness, a lower-case version of the library's CTheyreorThats routine has also been implemented:
3.6 NPC Action Support
OREnglish also contains for NPC actions. When used appropriately, the Standard library messages such as:
Are now sensitive to who is performing the action and may read as...
...if an NPC is performing the action. More on this in section 21 "ORNPCVerb".
An increasingly common feature of modern games distinguishes between the telling of the story, and the responses of the parser. Graham Nelson speaks of the different roles of the player, the protagonist and the narrator in his article "A Triangle of Identities" (found, among other places, in the DM4). The parser is, in effect, an unrecognized fourth identity usually separate from the story entirely and more concerned with the mechanics of the game. Messages from the parser seldom add to the story itself and serve a larger role in clarifying commands made by the player. It has become a recent, but well received practice of some authors to enclose parser messages in brackets.
ORBracketParserMsgs depends upon the OREnglish LDF, which was coded to leverage this entry if present. Simply including this module will wrap all parser generated messages in [brackets]. If, for some reason, the developer would like to turn off bracketing in the middle of a game, the global variable BracketedParserMsgs can be set to false to accomplish this. Additionally, the global variables BracketOpen, and BracketClose have been provided to set what text the messages will be wrapped in. For example, if the developer would prefer to wrap the parser messages in double greater-than/less-than characters, then the following lines of code could be added to the Initialise() routine:
The first module that OREnglish pulls in is the ORPronoun entry. This entry is based upon techniques discussed, or at least touched upon, in the article "Pronouns on Steroids". Review of that article is recommended. Contrary to its name, the ORPronoun module handles more than just pronouns. It also implements routines to facilitate subject-verb agreement in text that is applied to NPCs as well as the player, as well as add fluidity to your narrative by completely automating the use of pronouns.
5. 1 Absolute Pronoun Print Rules
ORProunoun provides several basic print rules to implement the various types of pronouns used in the English language. The names of these print rules are easy to remember since they match the first-person pronouns. These seem more natural than slightly more descriptive names such the (PronounAcc) or (PronounNom) examples from x62 or the DM4. The five lowercase versions of these are (I), (Me), (My), (Mine), and (Myself). Similarly, the five uppercase versions are (CI), (CMe), (CMy), (CMine), and (CMyself).
As you most likely could have guessed, these ten print rules are specifically tailored for use with animate objects. They utilize the fst() routine discussed previously in order to correctly determine the pronoun when the player is specified, but also check for gender and plurality. Individually, each print rule will produce one of eight possibilities. The (Myself) print rule, for instance, will consult the appropriate variables and choose the correct word(s) from the following list:
5.2 A Touch More Flexibility With Potential Pronoun print rules
The ten absolute print rules discussed above will always print the appropriate pronoun. However, there are times, especially when dealing with random events, that we do not know for certain that we want to print the pronoun. What is needed is an algorithm for determining if a pronoun will be understood or if the actual name needs to be used for clarity. This algorithm is implemented in the "TheXXX" print rules: (TheI), (TheMe), (TheMy), (TheMine), and (TheMyself). Additionally, these five print rules have capitalized counterparts: (CTheI), (CTheMe), (CTheMy), (CTheMine), and (CTheMyself).
As we progress in our tutorial, consider the following code snippet, modified from the examples given in the "Pronouns on Steroids</a>" article. Observe the potential confusion with two male NPCs that is avoided using these new potential print rules rather than the absolute ones:
For this step in our tutorial, we should change the location of the player to the Foyer in the Initialise() routine:
Run the tutorial to see that each turn, any of the following may be output (or other combinations):
Note that the print rules keep track of whom the pronoun is currently referring to so that confusing statements such as "He gave him a badge" are never produced. The rules are, however, smart enough to recognize and adjust for circumstances in which two objects can both be referenced by pronouns. Suppose the cleaner was a maid:
The following would then be entirely possible (odds: 1/16):
The butler coughs. The cleaning lady scratches her chin. He grimaces at her. She gives him
5.3 Noun-Verb Agreement
But for a few small nuances in the English language, all would be well with the fst() and ppf() routines.
The nuances have to do with noun-verb agreement. One example of this occurs only when three conditions are true:
1) The narrative is written in present tense.
2) We are speaking about a noun in the third-person.
3) The noun is singular.
When these three items are true, as they almost always are in an IF game where NPC actions are described, then we slightly modify the verb, usually by adding an 's' to the end. For instance:
You swim in the water.
The troll swims in the water.
Note the modified form of the verb swim in the second sentence, which matches the three conditions listed above.
Additionally there are special verbs, the so-called "verbs of being" in particular, that take different forms as well. Several routines have been implemented in the ORPronoun module to address the changing forms of verbs.
The first basic routine that we will discuss is the vrb() routine. It is important to note that the vrb() routine is not a print rule. Like the ppf() and fst() routines, when vrb() is called embedded within print statement it is usually preceeded by the (ig) print rule (discussed in section 9).
The vrb() routine was implemented to assist in achieving noun-verb agreement. The first parameter expected is the object that is performing the action. This completes the routines ability to check for the three conditions described above and select the verb appropriately.
The second and third parameters are the past tense and present tense forms of the verb being processed. For most verbs, these three parameters are all that is needed. Consider the following code snip:
In the narrative form of the overwhelming majority of IF games - that is, present tense/second person - fifty percent of the time, the output will be:
And the other fifty percent of the time it will be:
In the second case, the vrb() routine has added an 's' to the verb in order to make it agree with the noun. For the majority of verbs, this is all that is needed. There are many verbs that do not follow this form, however. "Fly," as an example, will incorrectly generate the word "flys," which is not correct. The fourth, optional parameter will be used instead if supplied. Changing the above example, to use the verb "fly" looks like this:
And outputs the following:
You fly in the water.
The troll flies in the water.
It is, of course, perfectly legal to specify the third form all the time, however it is generally considered to be wasteful since:
For games written in a future tense, there is also a fifth parameter used to accommodate that form of verb as well. Like the fourth parameter, this too is optional and the vrb() routine will attempt to generate the correct form of the verb if it is not specified. The same example that we have been working with so far, when run under a future tense context, will generate a somewhat prophetic statement such as:
The troll will fly in the water.
5.3.2 Other Basic Noun-Verb Agreement Routines (am, can, have)
There are other routines that have been included simply because of their usefulness when generating narrative adaptive text. The (am) print rule, in particular will appropriately choose and print the appropriate form of the verb "is":
Additionally, because of the frequency of their use, the print rules (can) and (have) have also been implemented in a similar manner.
5.4 Complete Noun-Verb Agreement Routines (IVerb, IAm, ICan, IHave)
Although there are a number of cases where the above routines are called for alone, it is generally more common to have a verb immediately follow the noun. For ease, IVerb(), (IAm), (ICan), and (IHave) were created. These routines do exactly what vrb(), (am), (can), and (have) do but also precede the verb output with a call to the (TheI) print rule and a space. Therefore, the following two code snips are equivalent:
Additionally, a capitalized version of each of these routines has also been created. That is, CIVerb(), (CIAm), (CICan), (CIHave).
Also, because the negative form of "can" is a single word in present tense ("cannot") and two words in past tense ("could not"), two additional rules have also been created: ICant and CICant;
In the maid/butler example in our tutorial, the text is written in present tense. Using these routines, we can more easily make the example print correctly regardless of the narrative's tense and person settings and the correct verb form is correctly chosen:
Wary reader’s may notice the (ig) print rule. We will cover that in the next section.
The ORTextFormating module contains a collection of print rules that facilitate the formatting of text.
6.1 (ig) – ignore print rule
The ignore print rule (ig) is used to suppress the printing of routine return values. This is primarily useful when embedding routines in print statements. Since all routines have a return value, embedding them in a print statement causes their return value to be printed as well. Consider the first potential line of text from the each_turn routine in the previous example without the use of the (ig) print rule:
This will result in the following output:
Notice the unwanted zero printed at the end. The (ig) print rule suppresses this.
At this point I feel compelled to give due credit. The (ig) print rule was not so much inspired by, as blatantly stolen from Roger Firth's article on print rules, dubbed withtheoh-so-tellingname"Print Rules>" . It is definitely worth review.
6.2 Italics, strong, highlight – style print rules
The Inform language has several "style" commands that change the way text is formatted to the screen. Use of these style commands is fairly straightforward. The following will print out a line of text with different styles:
The (italics), (strong), and (highlight) print rules allow formatting to be contained in a single print statement. The above code block is equivalent to the following:
Both examples produce the same results:
It should also be noted that not all interpreters are equal. The (italics) print rule uses the "underline" style because it appears as italics on specific platforms familiar to the author. Be advised, however, that other interpreters will render text printed with this print rule as underlined text which is, arguably, more appropriate.
Use of these print rules is straightforward, so no further tutorial example will be here. To see them used in the context of our tutorial, see Part C1 Section 2.
6.3 (arraystring) – character array print rule
Although there are many (not me!) that are of the opinion there is no real need to use arrays as text, the desire to is nevertheless present. The (arraystring) print rule has been introduced to help print the contents a character arrays. The following example will populate an array string, and then print it with the print rule.
The ORDynaString module leverages this print rule. Indeed, the ORDynaString class makes the need to use this print rule alone negligible so we will not extend the examples above. Our tutorial, as it stands now can be found here.
Table of Contents:
Table of Contents
Part A: Overview and Setup
Part C1 : Miscellaneous ORLibrary Modules